轉換一個場景,到另外一個專案!
同一個團隊、差不多的時期,另一個大型系統的遷移案。這個案子的失敗方式,跟前面那個剛好相反。前四天講的那個案子是:功能做錯了。多欄位、驗證失效、按鈕沒反應。這個案子是:
功能測試全部正常,但客戶認為不符合開發規範。
這裡有三個詞要先分開,不然後面會讀錯——而這三個詞的差別,是這整個系列最常被混用的地方:
| 回答的問題 | 誰說了算 | 可以模糊嗎 | |
|---|---|---|---|
| 需求 | 為什麼要做這件事? | 業務/使用者 | ✅ 可以,它本來就是願望 |
| 規格 | 做出來要長什麼樣、怎麼驗? | 寫規格的人 | ❌ 不行,模糊的規格驗不了 |
| 開發規範 | 要怎麼寫這段程式? | 維護這套系統的團隊 | ❌ 不行,但它常常只存在習慣裡 |
差別最好記的講法是:
需求 「我要能快速找到訂單」
規格 「查詢支援訂單編號、日期區間、客戶名稱;回應時間 2 秒內」
開發規範 「查詢邏輯放 Repository,不可以寫在 Controller 裡」
三句話講的是同一個功能,但沒有一句可以互相取代。
而這兩個案子壞的位置不一樣:
| 需求 | 規格 | 開發規範 | |
|---|---|---|---|
| 前一個案子 | ⚠️ 有,但只在客戶腦子裡 | ❌ 只有一份 Prototype | — |
| 這一個案子 | ✅ 清楚 | ✅ 沒問題(348/361 條測試通過) | ❌ 問題全部在這裡 |
兩件不一樣的東西,而它們的失效方式也不一樣。
上面那三行——需求、規格、開發規範——中間有兩次翻譯:
需求 →(翻譯一)→ 規格 →(翻譯二)→ 程式碼
翻譯一是把「我要能快速找到訂單」變成「支援三種查詢條件、兩秒內回應」。
翻譯二是把規格變成符合團隊寫法的程式碼。
在 AI 進來之前,這兩次翻譯常常是同一個人在做,而且是在寫程式的過程中順手做掉的。他讀了需求、心裡補出規格、然後照著他熟悉的團隊慣例寫下去。沒有人為那兩次翻譯估過工時,因為它們沒有名字。
而現在,翻譯二可以交給 AI——它產碼的速度是人的好幾倍。
翻譯一不行。「快速」是多快、「找到」是查得到還是查得準、要不要支援模糊比對——這些只有懂業務的人答得出來,而 AI 只會挑一個看起來合理的答案填進去。
所以真正的變化是這個:
翻譯二變快了十倍,翻譯一沒有變。
而以前它們是同一個人順手做完的,現在中間裂開了。
那個裂縫,就是前一個案子掉下去的地方——Prototype 被當成規格,等於宣布「翻譯一已經完成了」,但它其實一個字都沒翻。
而這一個案子掉的是另一個裂縫:翻譯二的依據錯了。 我們拿到的那份開發規範,不是那個團隊真正在用的那套。
這一段我原本寫得太簡略,讓它讀起來像「我們沒理規範」。事實剛好相反。對方確實提供了開發規範文件。 而且不是薄薄幾頁。是兩三百頁。我們也沒有把它當參考。我們把它做成了 AI 產出的品管站:規範裡的每一條,變成產碼之後要跑的檢核。
拿到兩三百頁的規範,還把它自動化了。在那個當下,我認為這件事沒有比這更紮實的做法。
所以當時我是很有信心的。我可以說:這個產出絕對符合規範。
然後客戶說不符合。
問題出在哪,是後來才弄明白的:
客戶真正在用的規範,跟他提供給我們的那份文件不一樣。
這不是「文件過期」那麼單純。真正的問題在那份文件的身分:
那兩三百頁是一份「交付文件」,不是「人們心中的規範」。
它存在的理由是合約裡要有這樣一份東西。它被寫出來、被交付、被歸檔。但沒有人真的拿它當日常判斷的依據。他們真正在用的那套,是團隊內部的共識、是習慣、是「大家都這樣寫」,從來沒有被寫下來過。
而這裡有一個很反直覺的後果:頁數多,反而更危險。
五頁的規範 → 你知道它不完整,你會去問
三百頁的規範 → 你以為規範已經講清楚了,你不會去問
我們沒有去問。因為兩三百頁看起來太像「已經講清楚了」。
更難看的是:我們把它自動化了。 而自動化只會讓這件事更嚴重。照著一份不是共識的文件做得越徹底,產出離他們心裡那套就越遠,而且過程中一路都是綠燈。
於是變成這樣:
我們的尺(照那份文件建的檢查) → 通過
他們的尺(沒有寫下來的那一套) → 60%
兩把尺都在量「合規度」,量出兩個數字。
而這件事最難處理的地方是:我們的檢查是綠的,而且它沒有壞掉。 它忠實地執行了我們拿到的那份規範。壞掉的是規範本身。
所以這個案子留下的那句話是這樣:
在 AI 開發的世界,你很在意的東西務必要講清楚、說明白。
否則 AI 會完成你的目標——而你看起來就是很奇怪。
而「講清楚」不等於「寫很多」。那兩三百頁就是最好的反證。
我後來認為真正該做的動作只有一個,而且它一點都不技術:
拿著那份文件去問一句:「這裡面哪幾條是你們真的在看的?」
這句話花不到十分鐘。而我們沒有問,因為文件看起來已經夠厚了。
這個系列從頭到尾都在講機制。把規則推到機器擋得住的那一層、把規格寫成可以翻成斷言的形狀、把檢查做成閘門。
而這個案子提醒我一件跟那個方向相反的事。
機制只能承載已經被講清楚的東西。它沒有辦法幫你發現「你以為講清楚了、其實沒有」。因為機制不會質疑它拿到的那份規範,它只會忠實地執行。以前這件事沒那麼要命,因為工程師會在做的過程中察覺不對勁,然後回頭問一句。而 AI 不會問。 它拿到兩三百頁,就照兩三百頁做,而且做得又快又整齊。
所以自動化提高的是「執行的忠實度」,不是「那份規範的正確度」。
而前者提高之後,後者的錯誤會被放得更大。
這就是為什麼我認為在 AI 時代,那個「花十分鐘去問一句」的動作變得比以前更值錢,不是更沒必要。
架構改善計畫(三階段,一週 + 兩週 + 一個月)留下的量化指標:
| 指標 | 現況 | 目標 |
|---|---|---|
| 架構合規度 | 60% | 95% |
| 程式碼重複率 | 40% | < 15% |
| 錯誤處理一致性 | 60% | 95% |
| 單元測試覆蓋率 | 85% | 90% |
| API 文件完整度 | 70% | 90% |
把兩個數字並排看:
單元測試覆蓋率 85% ← 測試是綠的
架構合規度 60% ← 但驗收不過
這個案子的測試361 條裡 348 條通過(96%)。功能大致是對的,剩下的十幾條是零星案例,不是結構性錯誤。
而客戶看的不是功能。

三階段的改善計畫裡,做的事情長這樣:
移除 60 幾個 try-catch。
每一支 Controller 都自己包 try-catch 做錯誤處理。改成統一由一個基底控制器處理,錯誤回應格式標準化。
移除 19 個查詢方法。
Service 層讀寫混在一起。同一個 Service 既有 GetXxx 又有 UpdateXxx。要拆成讀寫分離:查詢走 Queries、命令走 Service。
手動映射改成映射框架。
DTO 和 Entity 之間全部手寫轉換程式碼,所以重複率 40%。還有 Infrastructure 分層、Controllers 分層、ViewModels 與 Validators 建立⋯⋯
注意這些改動的共同點:沒有一項是在修 bug。
功能一開始就是對的。改的全部是結構。
這一段是重點。
你想一下:如果你叫一個 AI 寫一支 .NET 的 API Controller,沒有給它任何範例,它會寫成什麼樣子?它會寫成:
[HttpGet]
public async Task<IActionResult> GetData(...)
{
try
{
var result = await _service.GetDataAsync(...);
return Ok(result);
}
catch (Exception ex)
{
_logger.LogError(ex, "...");
return StatusCode(500, "發生錯誤");
}
}
每一支都自己包 try-catch。
這個寫法沒有錯。網路上九成的 .NET 教學都這樣寫,它能跑、能過測試、錯誤也有處理。如果你請一個沒看過這個專案規範的工程師來寫,他大概也會這樣寫。
問題是這個專案的規範是:**統一由基底控制器處理例外,Controller 不准出現 try-catch。**那份規範,AI 沒看到。或者說,它看到了,但它不在產碼當下的注意力範圍內。同樣的道理套用到另外幾項:
AI 產出的每一個決定,單獨看都是「合理的一般做法」。
加起來就是架構合規度 60%。
這就是我覺得這個案子最值得講的地方。
前一天講 UI 的沉默約束時提過一組對照。這裡要再加一行:
API 違反 OpenAPI → CI 紅燈
DB 違反 schema → 寫入失敗
UI 違反設計稿 → 什麼都不會發生
程式碼違反開發規範 → 什麼都不會發生,而且測試還是綠的
**最後這一行最陰險,因為它不只沒有紅燈,還有一個綠燈在誤導你。**348/361 條測試通過、覆蓋率 85%。這些數字全部是真的,而且看起來很健康。團隊每天看著綠燈開發,直到驗收。
我後來把這件事想成一句話:
你的測試在驗證「你自己定義的對」,驗收在驗證「客戶定義的對」,而這兩者之間沒有橋。
單元測試驗的是行為:輸入 A 得到 B。它天生驗不了結構。它不會告訴你「這支 Controller 不該有 try-catch」「這個 Service 不該有查詢方法」。
這個案子的驗收標準是「架構合規度」,那是很技術的東西。而前一個案子的驗收標準是客戶的印象——而印象只來自他碰得到的那一面。
那個案子的後端幾乎沒出事,但客戶最後認定「品質不好」,全部是因為前端驗證。你很難用「可是我們後端有三百條測試」去扭轉那個印象。
推論很現實:驗證資源不該平均分配。 不是每個模組都要 80% 覆蓋率,而是要問——哪一塊是客戶會直接碰到、碰壞了會形成印象的?(後面會講到同一個現象的另一個版本:安全網分布不均,會決定你敢改什麼。)
三階段的改善計畫:緊急修正一週、重要優化兩週、持續改善一個月。那是重工。 功能沒有變、使用者感受不到任何差別、對客戶來說沒有新增任何價值。純粹是把已經寫好而且會動的程式碼,改成符合規範的樣子。
這是最好的情況。如果那份規範是在專案後期才被拿出來對照,或者中間已經蓋了更多東西上去,成本會再翻倍。
先講一個事實:那份客戶的開發規範是存在的,是一份正式文件。但它從頭到尾只是一份文件——沒有任何一支腳本、任何一道 CI 檢查、任何一個 hook,會因為違反它而擋下來。
寫到這裡我想插一句實務上的觀察。
很多人第一次聽到「架構合規度」會覺得這是形式主義。**程式會動就好,管它有沒有 try-catch。**我以前也這樣想過。但做過幾個大型專案之後,我的看法變了:
規範不是為了好看。它是為了讓這套系統在交出去之後還改得動。而客戶(特別是要接手維護的客戶)非常清楚這件事。
**但這一篇的教訓是另一個方向:規範有價值,不代表「規範文件」有價值。**那兩三百頁裡,真正被拿來判斷的可能只有十幾條。而我們花力氣把兩三百頁自動化,不如花十分鐘問出那十幾條是哪幾條。
規範的價值不在它涵蓋了多少,在它有多少條是真的會被拿來判斷的。
前者可以外包給文件,後者只能靠問。
現在把兩個案子放在一起,用開頭那三個詞看:
| 前一個案子 | 這一個案子 | |
|---|---|---|
| 需求 | 有,但只在客戶腦子裡 | ✅ 清楚 |
| 翻譯一(需求 → 規格) | ❌ 沒有發生——Prototype 被當成規格 | ✅ 完成了 |
| 規格 | ❌ 只有畫面 | ✅ 沒問題(348/361 條通過) |
| 翻譯二(規格 → 程式碼) | 照 Prototype 做了 | ⚠️ 依據錯了 |
| 開發規範 | — | ❌ 拿到的不是真正在用的那套 |
| 結果 | 客戶說品質不好 | 客戶說不符開發規範 |
| 代價 | 187 張問題單、9 輪測試、停擺約三週 | 三階段重工 |
看起來是兩種完全不同的失敗。而它們斷在同一條線的兩端。
一個是翻譯一沒有人做——沒有人把「我要一個跟舊系統一樣的新系統」翻譯成「這一頁應該檢查哪些東西」。
一個是翻譯二的依據是錯的——我們照著一份文件翻,而那份文件不是他們心裡那套。
而兩件事的共同點只有一句:
AI 兩次翻譯都做得又快又好。
問題出在沒有人檢查它拿到的東西對不對。
這兩個案子,你猜哪一個先發生?
我到現在都覺得這個問題比答案本身有意思,因為不管答案是哪一個,推論都一樣不舒服:
兩種順序,同一個結局:上一個案子學到的教訓,剛好防不住下一個案子。
而這件事我在 Day 03 講過一次,只是那時候的尺度小得多——一份回顧擋不住下一輪的問題單。現在把尺度放大到專案層級,它長的是同一個樣子。
答案我明天講。(如果你已經猜到了,那你大概也猜得到明天那句「同一個原因」是什麼。)
本系列所有案例均經去識別處理,不指涉任何特定客戶、系統或產業。