前言:綠燈不等於對

我用一條 agentic 開發流水線,從零做完一個後台的「服務目錄」CRUD(Create/Read/Update/Delete,增刪查改)功能:一個內容管理系統(CMS)裡,管理員可以建立「服務」與「服務分類」,讓服務出現在使用者端 App 的列表裡。

結束時帳面很漂亮:單元測 772 全綠、build 通過、每個任務都過了兩段式 code review、最後還有一次整體 review。然後我把它部署到測試環境、用真實資料和真 App 一驗 —— 使用者 App 裡根本看不到剛建好的服務

這篇要講的不是「怎麼修這個 bug」,而是一個更值得記下來的觀察:單元測、code review、端到端驗證,三者各自只抓得到「不同層級」的錯,缺一層就會放掉一整類問題。


開發流水線:每個任務都派新的 subagent

我用的是 subagent-driven development:先 brainstorm 出設計、寫成 spec、再拆成一份逐任務的 plan,然後每個任務派一個全新、無歷史包袱的 subagent 去實作。它做完後,再派兩個獨立 reviewer —— 先審「spec 合規」(有沒有照規格、有沒有多做少做),再審「程式品質」。

為什麼要派新的 subagent

關鍵在 context 乾淨。實作者只拿到「這個任務的完整描述 + 周邊脈絡」,不繼承我的對話歷史,所以不會被先前的決策慣性帶偏。reviewer 同理:它不知道實作者「想做什麼」,只能讀「實際寫出來的 code」對照規格 —— 這正是抓得到落差的前提。

主控者(orchestrator)唯一的工作是「精準地餵 context」與「把關 review loop」,不自己寫 code。這保住了主控者的 context,也讓每個 agent 都聚焦。


第一層:兩段式 review 擋下的真 bug

下面這些全部躲過了單元測(因為 mock 餵的是理想資料),是被 reviewer 讀 code 抓出來的。

雷 1:跟 API 要「只給名字」,結果連 ID 都不給了

存一筆「服務」時,要記錄它屬於哪個負責人,所以需要負責人的 ID。我為了省流量,跟後端說「負責人那欄,只給我名字就好」。後端很聽話 —— 給了名字,把 ID 一起省掉了。

結果:打開服務要編輯時,負責人欄位帶不出來;按儲存時,也接不回負責人。

// 跟 API 說「owner 只要 name」→ 它就真的不給 id 了
GET /services?fields[owner]=name
// 程式想讀 owner.id,永遠是空的

這是很多後端共通的脾氣:你一旦指定「只要某幾欄」,它就真的只給那幾欄,連 ID 都不再附送,除非你把 ID 也寫進清單。單元測抓不到,因為測試用的假資料「自己」補了 ID,跟真實回應長得不一樣 —— 真環境一打才現形。一句話:精簡欄位時,別忘了把 ID 留著。 我之前也在某個框架上踩過同一家族的坑

雷 2:刪除按鈕在「檢查中」沒鎖,手快點兩下會刪兩次

刪一筆資料前,程式會先問後端「它底下還有沒有東西?」再決定能不能刪。問題在:等這個檢查回來的空檔,刪除按鈕沒被鎖住 —— 手快點兩下,就送出了兩次刪除。一般操作頂多重複,但刪除是回不去的。

reviewer 還順手抓到另一個:有個「只該內部用」的共用函式,被不小心開放成對外介面 —— 像把「員工專用」的工具擺到了客人櫃台上,外面其實根本不該碰它。

雷 3:跟專案裡「同類舊程式」長得不一樣

新寫的程式,每個負責人回傳的是 { ref, name } —— 少了一個 id;而專案裡既有的同類舊程式,一直都是 { id, ref, name }。當下不會壞,但只要有人照舊程式的習慣去用那個 id,就會拿到一片空白。這種「自己看沒問題、卻會害到照舊寫法的人」的不一致,是只盯著單一檔案的測試永遠照不到的。

被擋下的問題單元測抓得到?靠什麼抓到
精簡欄位害關聯 ID 變空❌(假資料自己補了 ID)reviewer 對照真實 API 行為
刪除沒鎖按鈕、內部函式外洩程式品質 review
跟同類舊程式格式不一致❌(單檔測不到)程式品質 review

第二層:綠燈之後,端到端才抓得到「功能沒達成目的」

雷全修完,772 測全綠、review 全過。但這些都只回答了一個問題:「程式寫得對不對?」 沒有任何一關回答另一個問題:「使用者真的看得到這個功能嗎?」

真環境一驗:服務沒出現在 App

部署到測試環境、用真 App 一看,新建的服務不在列表裡。追下去發現兩件事:

  1. 服務沒掛「地點(location)」。當初為了 YAGNI(You Aren’t Gonna Need It,別做還用不到的東西),表單刻意沒做 location 欄位 —— 但使用者 App 的查詢是 locations { services },只列「掛在某個 location 底下」的服務。沒地點 = 不存在於 App。
  2. App 的流程還少一層分類選擇,跟後台的資料結構對不上。

這兩點,單元測與 code review 都不可能抓到 —— 因為程式碼本身完全正確,錯的是「後台能管的資料」與「App 會呈現的資料」之間,有一道沒人驗過的縫。

管得到資料 ≠ 呈現得出來。CRUD 全對,不代表使用者看得到。這條縫只有把真資料丟進真 App 跑一遍才會現形。


三層互補:各驗一種「對」

把三層攤開,它們驗的根本是不同的東西:

驗的是哪一種「對」這次抓到的代表問題
單元測邏輯與契約對不對(mock 環境)URL 拼接、分支、回傳形狀
兩段式 review有沒有照規格 + 寫得好不好 + 隱性 bug關聯 id 恆 null、內部函式外洩、刪除沒鎖、格式不一致
端到端(真環境+真 App)功能有沒有真的達成使用者目的服務缺 location、App 少一層 → 使用者看不到

少了 review,雷 1~4 會帶著綠燈一路上線;少了端到端,「使用者看不到」會等到上正式環境、真人回報才爆。這也呼應我另一篇 LLM 驅動的 E2E 揪出後端回歸 的結論:自動化測試證明系統「沒壞」,端到端證明它「真的有用」。


都 AI 在寫 code 了,為什麼還會犯這些錯?

回頭看這些雷,沒有一個是「AI 不會寫 code」造成的 —— 每段 code 在它自己的檔案、在 mock、在 happy path 上都「看起來對」。錯之所以滑過去,是因為它們剛好落在 AI 結構性看不到的三個地方:

  • 局部正確 ≠ 全局正確:模型一次聚焦一個檔案,跨檔一致性(格式跟舊程式對不對得上)、真實 API 行為(精簡欄位會連 ID 都不給)、後台與使用者端之間的系統邊界,都在它視野外。
  • 自信輸出的盲點:生成的 code 流暢又篤定,正因如此「沒明說的假設」最容易被連人帶 AI 一起略過 —— 沒人會停下來質疑一段看起來很順的程式。
  • 驗證層級錯位:像「刪除前沒鎖按鈕」這種,都是「寫得出來、但要有人想到去驗」的東西;AI 寫得再快,也補不了一個沒人設計出來的驗證層。

換句話說,AI 縮短的是「把想法變成 code」的距離,沒縮短「確認 code 真的達成目的」的距離。 後面這段距離,正是這篇講的三層驗證在守。

結語

agentic 開發很容易讓人迷信綠燈:測試過、review 過、build 過,看起來就「完成」了。但這次的教訓很清楚 —— 那些綠燈全部在回答「程式對不對」,沒有一個在回答「功能有沒有達成使用者要的」。

所以流水線的最後一棒,永遠是把真資料丟進真環境、用使用者真正會走的路徑跑一遍。前面幾層越自動化、越快、越漂亮,這最後一棒就越不能省 —— 因為它是唯一一個會告訴你「漂亮的東西其實沒上場」的關卡。