~/VibeHandbook
免費 PDF

17 · 02

案例研究 2:一個帶認證 + 資料庫的小型 SaaS 工具

想法

一位自由職業設計師想要一個私人儀表盤,用來按客戶記錄可計費工時,並匯出每月的 CSV(Comma-Separated Values,逗號分隔值——一種純文本的電子表格檔案,任何電子表格應用都能開啟)。沒什麼花哨的,但它需要賬戶(這樣她的資料只屬於她自己)和持久化儲存。

Spec

認證和資料庫抬高了賭注,所以 spec 在邊界上變得更具體了:

A logged-in web app where a user can: sign up / log in with email,
create clients, log time entries (date, client, hours, note),
see a table of entries filtered by month, and download that month
as CSV. Each user only ever sees their own data. Mobile-friendly.

"Each user only ever sees their own data"看著像一行 UX 描述。它其實是把整個應用的安全模型壓縮進了九個詞裡。把它寫進 spec,意味著每次 AI 跑偏時我們都能指回這一句。

技術棧

對一個獨立開發者來說,制勝的一手是選一個讓認證和資料庫都成為託管服務、而非你自己寫的程式碼的技術棧。我們選了一個部署在 serverless 託管平臺上的 Next.js 應用,配上一個託管的 Postgres 資料庫,以及一個開箱即用的認證提供方,由它替我們處理郵箱登入、會話和密碼重置。能寫錯的程式碼越少,AI 能替我們寫錯的程式碼就越少。認證尤其是那種你幾乎從不想跟 AI 一起手搓的類別:它的失敗模式是靜默的,爆炸半徑是所有人的賬戶,而一個託管提供方早已被幾百萬次登入把邊界情況捶打過。

託管的部件都在外圈;你自己掌管的那唯一一條規則在中間——每一條查詢都按 user_id 過濾,於是一個使用者永遠讀不到另一個使用者的資料行:

 ┌──────┐          ┌─────────────┐         ┌──────────────────┐
 │ 使用者 │─ 登入 ──▶│ AUTH        │─ 使用者 ─▶│  NEXT.JS APP     │
 └──────┘          │ (託管)      │  ID     │                  │
                   └─────────────┘         │  每次查詢:       │
                                           │  WHERE user_id=? ─┼──┐
                                           └──────────────────┘  │
                                                                 ▼
                                                       ┌──────────────────┐
                                                       │ POSTGRES (managed)│
                                                       │ clients · entries │
                                                       └──────────────────┘
            ↑ 託管 = 更少出錯空間                 ↑ 你來負責守護的唯一邊界

關鍵的 prompt

我們讓認證提供方自帶的模板來挑大樑,然後指揮 AI 在它之上疊加業務邏輯:

We're using [auth provider]'s Next.js starter. Add a Postgres
schema with two tables: clients (id, user_id, name) and
time_entries (id, user_id, client_id, date, hours, note). Every
query MUST filter by the logged-in user's id from the session.
Generate the migration and the typed data-access functions.

"MUST filter by user_id"這一行是整個專案裡最重要的一句話。我們在幾乎每一個觸及資料的 prompt 裡都重複了這條約束,因為一個多使用者應用裡最可怕的單一 bug,就是一個使用者看到了另一個使用者的資料行。打字時這種重複感覺是冗餘的;可正是這份冗餘救了你,因為模型並不記得這條約束在一個個獨立的 prompt 之間有多要緊。

至於匯出:

Add a /api/export route that takes a month (YYYY-MM), pulls the
logged-in user's time_entries for that month joined to client
names, and streams a CSV download. Reject the request if there's
no valid session.

障礙

在測試時,我們建立了兩個賬戶,然後發現賬戶 B 能在一個下拉選單裡看到賬戶 A 的客戶。這正是我們害怕的那個 bug。我們沒有讓 AI"修好它",而是先讓它把問題證明出來:

Account B is seeing Account A's clients. Show me every database
query in the codebase that reads the clients table, and for each
one tell me whether it filters by the session user_id. Don't fix
anything yet — just audit.

這次審計浮出了一個查詢——那個下拉選單的載入器——它是在我們加上那條約束之前寫的,於是溜了過去。我們讓它補上了缺失的過濾條件,然後又要了一道守衛:

Add a single helper that every read goes through, which takes the
session and injects the user_id filter, so no future query can
forget it. Refactor the existing queries to use it.

這把一次性的修復變成了一個結構性的保證。接著我們讓這個保證變得可測試,因為一道你沒法驗證的守衛不過是一種指望:

Write a test that creates two users, has each create a client,
then asserts that user A's session can never read user B's client
through any of the data-access functions.

教訓:當 AI 引入一個安全 bug 時,別隻修補這一個例項——要指揮它去消除這一整類錯誤,然後用一個一旦有人重新開啟就會大聲失敗的測試,把這一類鎖死。

上線

我們灌入了一個月的測試資料,匯出了 CSV,在電子表格裡開啟它以確認數字和編碼都正確,然後設定了一個強資料庫密碼,並把憑據從所有本地檔案裡輪換出去。我們部署到了 serverless 託管平臺,在它的控制面板里加上了生產環境的環境變數,然後把 給了她。她用一次真實的註冊完成了自助入駐。整個構建只花了一個週末。

想離線閱讀?

免費下載整本書的 PDF 或 EPUB。