一段用 Claude Code 對話式開發的記帳 App 建置紀錄。
緣起
我家人多年來都在用一個 Android 記帳 App「帳務小管家 ZERO v4.7」記帳。資料完整、分類細緻、運作穩定,但有兩個痛點:
- 版本停止更新:手機壞了或換手機,只能綁在 Android 手機上
- 無法多人共用:家庭開支想一起記就尷尬
15,000+ 筆累積的交易、十幾種帳戶、四十多個專案分類 — 捨不得換、也不想重來。所以決定:自己做一個能完整繼承原資料、加上雲端同步的版本。
App 取名 Song 帳本。
設計取捨
幾個關鍵決定:
1. PWA(漸進式網頁應用)而不是原生 App
- 一個網址就能開,不用上架商店、不用簽名
- iOS / Android / 桌機平板通吃
- 「加入主畫面」就有 App 般的全螢幕體驗
2. Offline-first(離線優先)
未登入時 = 純本機記帳,跟以前一樣;登入後 = 雲端同步「疊加」上去,可選哪本帳要同步。這樣家人不熟雲端也照樣能用,熟的可以多裝置同步、跟家人共用一本帳。
3. 單一 HTML 檔部署
最終打包成一個 1.1 MB 的 index.html — 內嵌 SQLite WASM 引擎、所有邏輯、CSS、字型。Cloudflare Pages 拖一下就更新,沒有 build 鏈、沒有 npm 依賴。
4. 後端用 Supabase 免費方案
- Postgres + Auth + Realtime + Row Level Security 一站搞定
- 免費額度 500 MB / 月,目前用不到 2%
- 配 Gmail 自訂 SMTP 寄登入碼
完成的功能
| 類別 | 功能 |
|---|---|
| 記帳 | 支出 / 收入 / 轉帳、兩層分類(大類 / 子項)、專案歸戶、計算機輸入 |
| 多帳本 | 每本帳獨立儲存、互不干擾 |
| 多幣別 | 外幣帳戶用原幣記、報表自動換算成台幣 |
| 雲端同步 | Email OTP 登入、Realtime 即時推播、Last-Write-Wins 衝突處理 |
| 離線佇列 | 沒網路時照記、連上網自動補上傳 |
| 帳本分享 | 擁有者 / 可編輯 / 唯讀三種權限 |
| 資料匯入 | 可直接吃原 App 的 .db 檔案 |
| 報表 | 月報、年報、趨勢、專案、行為分析(含 MoM / YoY) |
| 雙版面 | 手機單欄、桌機/平板自動切儀表板(左欄首頁常駐、右欄切換) |
用 Claude Code 對話式開發
整個專案我幾乎沒有手寫一行程式碼 — 全程透過自然語言對話、由 Claude Code 動手實作。流程大致是:
- 我描述問題或丟一張截圖
- Claude 定位到對應程式碼、解釋現況、提方案
- 我確認方向 / 微調 / 反問
- Claude 動手改、跑語法檢查、必要時用瀏覽器 preview 看效果
- 我看一下截圖、決定要不要 commit
- Claude commit,留下精準的歷史
幾個讓我印象深刻的時刻:
- 我發現「現金-美金餘額是負的」,貼了原 App 截圖。Claude 看了 .db 結構,發現匯入器把原幣金額跟歷史台幣換算混淆了,重寫成「存原幣 + 當筆匯率,報表用兩者相乘」,最後把基準數對到原 App 截圖到位數(美金 762.76 × 30 = 22,882.80)。
- 月報「淨退」大類(退款 > 支出)的長條跑出畫面外,數字顯示「-0%」。Claude 找到根因(百分比沒鉗位),順手把其他三條長條也加上防禦性
Math.max(0,…)。 - 凌晨 0–8 點「複製交易到今天」會變成前一天的日期,因為
toISOString()回的是 UTC 而台灣是 UTC+8。Claude 找到todayStr()helper,改用本地時區,順便把另一處一樣 bug 的wsStr一起修。 - 我抱怨「手機 vs 桌機螢幕差異很大、桌機兩側空空的」,Claude 幫我做了一個寬螢幕儀表板:CSS Grid 切兩欄、左欄首頁常駐、右欄跟著 tab 切換。實作過程中還抓到一個 bug — boot 時的 inline style
display:block蓋過了 media query 的display:grid,整個 grid 沒生效。 - 我問「原 App 的專案有 ID 和說明文字,你的匯入器抓了嗎?」Claude 去 query 了
mymoney.db的PROJECT_SET表,發現確實有PROJECT_NOTE欄位被忽略了,補上之後還做了一個「從原 .db 單獨匯入專案說明」按鈕 — 不洗交易資料就能補上。
整個過程約 30+ commits、跨 4 天集中開發。 我幾乎不用碰程式碼,但每一個技術決策都掌握在自己手上。
一些有趣的細節
回沖(負數金額的支出)
原 App 允許「-300 元支出」當作沖回(例:同事退款)。匯入時數值處理正確、但顯示和正常支出沒區別 — 我以為被當成普通支出了。Claude 確認資料層面正確,視覺上改成:
- 一般支出:-$300 紅色
- 回沖:+$300 綠色(同一筆支出、用相反符號和顏色標示)
月支出合計自動扣回。
報表 MoM/YoY 邊界
當上月為負數(淨退)時,(curr - prev) / prev × 100 的公式會因為除以負數而符號翻轉,顯示「下降 3728%」之類的鬼話。改成:
prev ≤ 0→ 顯示「—」(基期無意義)curr < 0→ 顯示「淨退」(當期是退款月)- 其他 → 正常 ▲/▼ 百分比
災難復原?
不需要設定自動備份。多裝置同步本身就是分散式備份 — 每台裝置的本地 IndexedDB 都是一份完整副本,Supabase 真的壞掉,任何一台同步中的裝置「啟用同步」就會 re-push 回去。
加上偶爾手動匯出 JSON 存到 OneDrive,就是雙保險。
結果
- 主網址:https://song-ledger.song.tw/(自訂網域)
- 部署:Cloudflare Pages(免費額度日 10 萬請求,用很少)
- 後端:Supabase 免費方案(用不到 2%)
- 家人在用、我自己也在用、跨裝置即時同步
順帶生了兩本手冊:
- 家庭使用手冊(PDF,9 頁,附截圖)
- 管理者維運手冊(PDF + Markdown,記錄部署流程、雲端服務位置、疑難排解)
反思
- Offline-first 是正確選擇。家人不一定每天有網路、不一定想登入;不強迫雲端讓 App 對所有人都好用。
- 少即是多。沒有用任何 framework — React / Vue / Tailwind 一個都沒有,純手寫 vanilla JS / CSS。1.1 MB 一檔搞定,啟動快、改起來也快。
- AI 不會取代你的判斷。所有關鍵決定(要不要做 keep-alive、UI 走向、哪些 bug 先修、要不要寫自動備份)都是我做的;Claude 是執行 + 細節驗證的合作者。決策權永遠在你手上,這也是這套工作流最舒服的地方。
- 資料才是核心資產。花最多力氣的不是 UI、不是同步,是「正確匯入 15,000 筆歷史資料」這件事 — 對到位數、處理多幣別、回沖、專案說明,每一項都要驗證。
如果要再做一次,我會更早開始用 Claude Code 對話式開發 — 一開始我自己手寫了一些 code、改了又改、效率反而不高。
Stack 速查
| 層 | 技術 |
|---|---|
| 前端 | vanilla JS / CSS / HTML,單檔 1.1 MB |
| 內嵌 | sql.js(SQLite WASM)讀原 App .db |
| 後端 | Supabase(Postgres + Auth + Realtime + RLS) |
| 部署 | Cloudflare Pages(自訂網域 song.tw) |
| 同步 | Supabase Realtime postgres_changes + 離線 outbox |
| 開發 | Claude Code 對話式 |
| 文件 | reportlab 產 PDF、Microsoft JhengHei 中文字型內嵌 |
寫於 2026 年 5 月底


沒有留言:
張貼留言