
文章同步發表在我的個人 Blog
做了 28 天,AI 最常在哪裡寫錯?
昨天 Day 28 處理設定頁的語言選項。今天不寫新功能,回頭讀每一輪結束時記在 PROGRESS.md 的「踩到的坑」,整理 AI 實際出錯的地方,再回答 Day 1 開始就有的問題:兩個 Agent 分工,結論是什麼。
先講結果:每一輪的流程是七個步驟,寫在第 1 節。我在 PROGRESS.md 找到 26 條踩坑,分成環境與工具、iOS 與測試框架行為、Firestore 與規則、規格和既有規則衝突、改完沒有重新驗證五類。這 26 條大多是要實際跑過才知道的行為。兩個 Agent 的比較,我沒有數字可以給,原因寫在第 4 節。
後面的踩坑都發生在這個流程裡,所以先把流程寫出來。這是 AGENTS.md 規定的做法,Claude Code 和 Codex 共用同一份:
BACKLOG.md 取目前里程碑第一個沒勾的項目,一輪只做一個。需求有疑義,寫進 docs/QUESTIONS.md 並跳過,不自己假設。test_AC_TOPIC_03_replyByOverdueNotifiesBoth。[codex] 的純邏輯項目交給 Codex,在獨立的 git worktree 做,合併前由 Claude Code 對照驗收條件讀 diff。./scripts/verify.sh,最後一行要印出 VERIFY: PASS。畫面相關的項目,另外截 Light、Dark、ax3 三張圖審視。PROGRESS.md,寫做了什麼、卡在哪、下一步,踩到的坑也記在這裡。連續兩輪同一個錯誤修不好,就寫進卡點,改做下一項。第 7 步的記錄,就是下面 26 條踩坑的來源。
分類是我自己讀完每一條之後分的,有主觀成分。條數是我在 PROGRESS.md 找得到的,不是完整清單。
| 類型 | 條數 | 例子 |
|---|---|---|
| 環境與工具 | 9 | 新增測試檔後沒重跑 xcodegen generate,UI 測試顯示執行 0 個也不報錯 |
| iOS 與測試框架行為 | 7 | iOS 26 的確認對話框是 popover,沒有取消按鈕,同一個按鈕在元素樹裡出現兩層 |
| Firestore 與規則 | 6 | Timestamp 和 Date 來回轉換的次微秒誤差,被規則當成改了 createdAt |
| 規格和既有規則衝突 | 2 | 新集合 users/{uid} 開放後,既有規則測試會失敗 |
| 改完沒有重新驗證 | 2 | 上午「瑕疵修掉」後沒有重跑,晚上 6 個 UI 測試失敗 |
另外還有 Day 21 提過的四個缺口(打錯字的卡片不能改、App 圖示是空的等),那四個是「需求裡沒有寫到」,不在這 26 條裡。
環境與工具:新增一個測試檔之後,Xcode 專案不會自己多出那個檔案,因為專案檔是 XcodeGen 從 project.yml 產生的。不重跑 xcodegen generate,測試報告是執行 0 個、沒有錯誤。測試顯示沒有錯誤,不代表有跑到東西。
iOS 與測試框架行為:Section 上設 .accessibilityIdentifier,會蓋掉裡面子元素的 identifier,UI 測試找不到按鈕。處理方式是把 identifier 放在內容容器上。這種問題程式本身沒有錯,要用 UI 測試跑一次才看得到。
Firestore 與規則:規則裡寫 list[0:n] 檢查陣列只追加,n 是 0(空陣列第一次追加)的時候,合法的追加被拒絕。加上 before.size() == 0 || 的保護之後通過。
規格和既有規則衝突:規格寫新集合叫 users/{uid},但既有的規則測試把 users/uid-alice 當成「不該被開放的路徑」,開放之後那個測試會失敗。專案規定不能刪或弱化既有測試,所以集合改名 userHouseholds。AI 照規格寫會遇到衝突,是因為規格沒有對照既有測試。
改完沒有重新驗證:9/30 上午修掉幾個瑕疵之後,沒有再跑 ./scripts/verify.sh,晚上才發現 6 個 UI 測試失敗。Day 28 也有同一類:語言區段放在「提醒」之後,把版本列擠出畫面,3 個既有測試用版本列當載入依據,找不到。這次測試不能改,所以改 App,把語言區段移到版本列之後。
計畫裡的分工是 Codex 規劃與審查、Claude Code 實作與寫作,並排了 Day 3、11、21、29 四個比較日,要同一個小任務兩邊各做一次,記錄時間、來回次數、錯誤類型。
實際上,比較日沒有做成實驗。Day 21 已經寫過:沒有記錄時間和用量,所以不比誰比較快,也不比誰比較省。這一篇也一樣。上面 26 條踩坑,記錄裡沒有標是哪一個 Agent 寫出來的,所以也不比誰錯得比較多。
能說的只有記錄裡有的事:
acceptance.md 讀的,沒寫進去的東西,兩邊都不會發現少了。從這些記錄能寫的結論只有兩句:互相審查抓到的是設計、時序和規則的問題;需求沒寫到的缺口,是實際使用才發現的。
現在每一輪結束前,都照這四個檢查做:
./scripts/verify.sh,並把最後一行 VERIFY: PASS 或 VERIFY: FAIL 印出來。xcodegen generate,再看測試數量對不對。上面四個是每輪結束前做的。下面八件事是寫的過程中容易漏掉的,都來自 PROGRESS.md 的記錄,每一件後面接當時的處理方式。
null 或 [] 到沒有這個欄位的舊文件,Firestore 規則會看成欄位被改動,擋掉一般的確認與修改。處理方式是沒有值就不寫入,未完成用 FieldValue.delete()。規則改完,要拿舊格式的文件再測一次。Timestamp 和 Date 來回轉換會有次微秒誤差,規則會當成 createdAt 被改。處理方式是統一取整到微秒。setData 會等伺服器確認,離線時一直等不到。處理方式是改用 completion 版本。codex exec 在背景會等 stdin,所以指令後面要加 </dev/null。兩個負責畫面的子 Agent 曾因串流逾時中斷,用 SendMessage 接續後完成。generic/platform=iOS,要指定裝置並加 -allowProvisioningDeviceRegistration;Xcode 登入失效要重新登入;裝置要開啟開發者模式。免費的 Personal Team 簽章會到期,到期前要重裝。SDLC(Software Development Life Cycle,軟體開發生命週期)把開發分成需求、設計、實作、測試、審查、部署與維運。這 30 天,AI 讓實作那一段變快,其他階段沒有跟著變快。下表把記錄放進每個階段,最右欄是從缺口反推的做法:
| 階段 | 這 30 天做了什麼 | 記錄裡的缺口 | 一開始就要有 |
|---|---|---|---|
| 需求 | Day 2、6 做投票頁,Day 13 結果讓第一版重做 | 第一版做完才發現自己沒在用;Day 21 的四個缺口是需求沒寫到 | 先用最小的方式驗證需求,驗收條件寫成有編號的條目 |
| 設計 | Day 10 架構文件和 ADR(Architecture Decision Record,架構決策紀錄),Spectra 的 proposal、design、specs | 規格寫 users/{uid},和既有規則測試衝突;Day 19 規格寫「只有 note 卡可完成」,實作時才發現不對 |
平台限制列成表;規格寫完先對照既有測試和規則;規格錯了,規格和程式一起改 |
| 實作 | Claude Code 實作,Codex 做純邏輯,邊界寫在 AGENTS.md |
新增測試檔沒重跑 xcodegen generate;背景指令等 stdin |
禁止事項寫成文件;一輪一個項目;交給另一個 Agent 的工作限定在純函式或單一資料夾 |
| 測試 | 測試名稱帶驗收條件編號,另有規則測試、UI 測試、變異測試、verify.sh |
空測試顯示通過;Day 24 的規則有 2 個條件拿掉後測試仍然全過 | verify.sh 第一天就有,每輪印最後一行;懷疑測試守不守得住時,故意改壞一處,確認測試會失敗(Day 20) |
| 審查 | Codex 唯讀審查 7 份,列出 27 條 P1 | 審查對照 acceptance.md,沒寫到的缺口沒有人看得到 |
誰寫的程式碼,由另一個 Agent 審;審查之外,另有實際使用的回饋 |
| 部署與維運 | Day 18 裝實機,Day 26 簽章到期重裝,Day 28 處理回饋 | 第一次裝實機在 Day 18 | 最早的里程碑就裝到實機;簽章到期日排進計畫;回饋有固定的分類流程 |
表格最右欄重複出現的是三件事:驗收條件有編號、驗證腳本自動執行、盡早用實機。
| 項目 | 結果 |
|---|---|
| 踩坑 | 26 條,分五類 |
| 兩個 Agent 的比較 | 沒有時間與來回次數的數字 |
| 每輪結束前的檢查 | 四項 |
| 寫的過程中要留意的事 | 八件 |
| SDLC 對照 | 六個階段,每個階段一項缺口與對應做法 |
Day 30 回顧這 30 天:投票結果和做出來的東西對照,以及接下來怎麼繼續使用。
官方文件
project.yml 產生 Xcode 專案list、Timestamp 等型別accessibilityIdentifier