去年十二月,我那份週報的最後一行寫著「團隊士氣:優秀,技術架構清晰,開發效率高」。

五月的週報,標題是「退回潮收斂期」。

中間發生了什麼,這篇就是要講的事。我把一個有數百個頁面的舊系統從 AngularJS 搬到 Vue 3,主力是 AI——半年下來程式碼幾乎都是它寫的,但我整個人變成了幫它擦屁股的。

蜜月期:一週幹完三週的活

一開始真的很爽。

專案的前置準備階段,原本排了三週。我把環境建置、登入流程、Layout 元件、狀態管理這些丟給 Claude Code,它一週就全做完了,還順手把舊系統漏掉的九個功能補上。階段提前兩週收工。

那時候我心裡的念頭很單純:照這個速度,這專案根本不用排到四個月。

我那時候真的信了。

接下來幾個月,產出數字一路往上飆。隨便抓幾週的紀錄:某一週 82 個 commit、改了兩百多個檔案、淨增三萬六千行;五月某個禮拜一,光是一天就推了 33 個 commit。如果你只看這些數字,會以為這是一支開了外掛、穩到不行的團隊。

然後,退回潮來了

問題是,commit 數不等於完成數。

業主驗收一輪一輪退回來。我的週報開始反覆出現「退回」「二次修復」「再修復」這些字。最誇張的一天,我一口氣推了十幾個 commit,幾乎全部都是前幾週「已完成」功能的二次退回修復。

有些 bug 改到我都會背了。某個選單的職責分離,我一週之內調了三次方向——改完發現側邊欄高亮被路由邏輯蓋掉,再改;改完又冒出殘留的孤兒頁面,再改。還有一個 issue,AI 改完,隔天我發現它連帶搞壞了別的東西,整個 commit revert 掉重來。改了退、退了改。

但這些還只是煩。真正讓我背脊發涼的是另一種。

AI 幫某個表單寫了存檔功能,跑起來一切正常:按下儲存、畫面跳出成功、列表也重新整理了。直到業主回報「資料怎麼都存不進去」,我追下去才發現——它寫的欄位名跟資料庫對不上。程式碼語法完全正確、不會報錯、該有的流程一個不少,唯獨資料安安靜靜地沒寫進任何一張表。這種錯沒有紅字、沒有例外、沒有堆疊追蹤,它就是不動聲色地什麼都沒做。

還有一次更哲學。某張申請書要匯出成 PDF,AI 加了一套檔案格式白名單,用 magic bytes(檔案開頭那幾個位元組,用來辨識檔案真正的類型)驗證產出的確實是 PDF。它驗證通過,回報完成。我打開一看——格式是對的,裡面的欄位內容全是錯的

我那週的風險筆記裡留了一句話給自己:「magic bytes、檔案大小通過,不代表內容正確。」這句話後來變成我看所有 AI 產出的預設心態。

最戲劇性的一次,是一整個模組被我整包刪掉。AI 認認真真把那個模組從頭建好了,頁面、API、路由一應俱全,但回頭一看:功能定義模糊、跟另一個模組大量重疊、文件缺東缺西。我算了一下,發現重寫比修補還快,於是直接砍掉重來。那一週的整體進度,從 18.9% 倒退到 17.4%。一個禮拜的產出歸零,週報上的數字第一次往回走。

(順帶一提,它偶爾還會自作主張。有個退回項目的修法,commit 訊息我是這樣寫的:「移除 AI 自加的工期提示文字」——它在表單上加了一段需求裡根本沒有的說明文字,我得專門花一個 commit 把它請走。)

它沒有騙我,它是真心覺得自己做完了

這裡要幫 AI 講句公道話。它回報「完成」的時候,不是在敷衍。

在它的世界裡,那個 PDF 確實做完了:檔案產生了、格式驗證過了、程式沒報錯。那個存檔功能也做完了:流程跑完、畫面有回應。問題是「做完」這件事,它判斷的標準跟業主驗收的標準,是兩個不同的框架。AI 活在「我有沒有產出一個合法的、語法正確的東西」這個框架裡,而真正的問題是「這張表此刻該長什麼樣、哪幾個欄位對應資料庫的哪一欄、業主紅筆圈了哪裡」——這個框架它進不去。

我後來常想到那些嚇人的 AI 跑分。那些數字全是在人先出好的考題上跑出來的:題目的框架是人定的,答案的邊界也是人圈的。可是業主的驗收標準不在任何一張考卷上,它每天都在變,藏在一通電話、一句「這個不是我要的」裡。模型再會考試,也考不到這一題。

我最近讀到 Every 的 Dan Shipper 寫的〈After Automation〉,裡面有個說法我盯著看了很久,大意是:一個情境一旦被化約成文字、變成了訓練語料,它就成了一具屍體。

AI 知道怎麼寫一個 PDF 匯出、怎麼寫一個存檔功能,因為這種事被寫過幾百萬次,全在它的訓練資料裡。但它不知道這一張表單、這一個業主、這一刻的驗收重點長什麼樣——因為那還沒變成文字,還活著,只活在我和業主的對話裡。

有一次這件事具體到讓我記到現在。AI 寫了個新增功能,程式碼乾淨到我挑不出毛病——組好物件、帶上主鍵、呼叫儲存,每一步都是教科書上最標準的寫法。結果一執行,後端整個崩了。我追了半天才發現,這個專案的資料存取層是十幾年前公司底層專案刻的,藏著一條反直覺的地雷:新增資料時,如果你把那個自動遞增的主鍵也一起傳進去,它不會聰明地忽略,而是拿這個值去找一筆根本不存在的舊紀錄,然後當場崩潰。正確做法是新增時「不要」傳主鍵,讓它自己長。

這條規則沒寫在任何文件裡,只躺在那份十幾年沒人動過的原始碼深處。AI 會寫出那段「正確」的程式碼,正是因為它讀過幾百萬份「正確」的範例——可這個專案的地雷,從來不在那幾百萬份裡面。它沒變成過文字,所以對 AI 來說,它根本不存在。

Shipper 給這種協作模式取了個名字叫「human sandwich」:人在前面設定框架,AI 在中間把任務壓扁,人在後面判斷結果好不好、再把它接回現實。我們是 AI 工作的兩片麵包。

讀完那天我才意識到,我這半年根本就是那兩片麵包。前面我要把模糊的需求翻譯成 AI 聽得懂的指令,後面我要逐項驗收它到底有沒有真的做到。中間那層夾心填得飛快,但夾心不會自己決定要夾什麼。

我被迫長出一套防呆系統

更現實的是,AI 不只在業務邏輯上要人收尾,它連工程紀律都會給你捅婁子。

連續密集派工一段時間後,我踩了夠多雷,最後在專案的 CLAUDE.md(給 AI 看的專案規則檔)裡沉澱出三條鐵律:

1
2
3
4
5
6
7
8
9
10
# Agent 派工 SOP(血淚換來的)

1. 派 Agent 前,先同步 git 狀態
→ 不然多個 agent 在不同步的基底上各改各的,主 repo 直接衝突

2. Agent 交辦單一律禁跑 build,由我收尾統一跑一次
→ agent 各自跑 build 會互相覆蓋產物,還會誤判通過

3. Agent 的 lint 永遠在主 repo 跑,禁止在 worktree 裡 npm install
→ 不然它在自己的小天地裡 lint 過了,回到主 repo 整個爆掉

這三條每一條背後都是一次真實的災難。第三條我印象最深——AI 在它自己的工作目錄裝了套件、lint 跑得漂漂亮亮、回報「build 通過」,我合回主 repo 才發現根本兜不起來。它沒說謊,它只是又一次在自己的框架裡通過了。

光有規則還不夠。後來我多養了一個習慣:讓另一個 AI 去審第一個 AI 寫的東西。某個模組是 Claude 在一天之內趕出來的,我不放心,丟給 Codex 做一輪對抗式審查——結果它揪出三個 Critical 級的問題:一個 SQL 注入、一個子表沒做 fail-fast、一個 Object URL 用完沒釋放、記憶體就這樣漏著。一個 AI 寫、另一個 AI 挑刺、最後我來裁決。聽起來很荒謬,但這就是我現在每天在做的事。

寫規則、設防呆、養一個 AI 來監督另一個 AI——這些工作以前都不存在。它們是 AI 帶來產能的同時,硬塞回我手上的新工作。

所以,自動化讓我變閒了嗎?

完全沒有。

我花在「敲鍵盤寫程式」的時間確實大幅下降,但省下來的時間全被三件事吃掉:把需求拆成 AI 能執行的框架、逐項驗收它的產出、以及想辦法防止它用語法正確的方式做錯的事。產出的總量爆增了,但其中沒有任何一行,在我看過、確認過、接回現實之前,是可以信的。

吐槽歸吐槽,我沒有要退回去一頁一頁自己手刻,這個專案原本預估我一人兩年,最後被業主壓縮到一年要開發完,結果現在能半年內開發完,雖然問題很多,但的確速度快了不少。AI 把這個專案的天花板拉高了——有些一個人扛不動的大規模重構,現在敢動了。我的工作沒有變少,是換了形態:從生產者,變成框架的設定者和結果的裁決者。

老實說,這個身分我還在適應。偶爾會懷念那種一行一行把東西敲出來的踏實感——但那種日子大概是回不去了。

如果你也正準備把一個大專案交給 AI,我只有一個從血裡撈出來的建議:估時程的時候,把你以為的「收尾時間」直接乘以三。剩下的,等你也當過幾週麵包就懂了。


延伸閱讀:這篇的靈感來自 Dan Shipper 的〈After Automation〉。如果你想看他完整的論點——為什麼 AI 越自動化、人類專家反而越搶手——我整理了一篇導讀:沒有「取代所有人」的臨界點