如果前面 24 天讓你覺得「只要把複雜度預算、架構測試這些機制都建好,過度設計問題就解決了」,今天這篇要來澆一盆冷水。這不是為了唱反調,而是因為誠實面對一套方法論的邊界,本來就是這個系列該做的事——一套講得太滿的方法論,遲早會在某個場景失靈,而讀者信任的崩塌往往就發生在那個時刻。
回顧一下 Day 19-20 講過的架構測試跟複雜度預算:架構測試可以斷言「Domain 層不能依賴 Infrastructure 層」,複雜度預算可以斷言「新增一個欄位,改動檔案數不能超過某個門檻」。這些機制的共同性質是——它們都需要先有一條被寫死的規則,然後機制才能去驗證程式碼有沒有違反這條規則。
問題在於:這條規則本身是誰訂的、根據什麼訂的、訂得合不合理,機制完全不會幫你檢查。如果我把「改一個欄位最多動 3 個檔案」這個數字寫死,套用到 Day 24 討論的留言功能,機制會忠實地把每一個合理但略微複雜的改動都標記成「超標」——機制不知道這是誤判,因為它只認得數字,不認得「這條需求的本質複雜度比較高」這種語意判斷。規則能不能訂對,仍然是人的責任,機制只能保證規則被貫徹執行。
素材裡真實出現過的一個例子很有代表性:過度設計版本裡「標題不可為空」這條規則,同時寫在 TitleNotEmptySpecification、CreateArticleValidator、ArticleAggregate 建構子三個地方。三個地方都沒有邏輯錯誤,行為上完全一致,架構測試也抓不出問題——因為架構測試檢查的是「依賴方向對不對」,不是「同一條規則有沒有被檢查了三次」。
要抓出這種重複,需要的是一個人讀懂「這三個地方在做同一件事」,這是語意層次的判斷,不是結構層次的規則能自動偵測的。可以寫一個更複雜的靜態分析去找「疑似重複的例外拋出點」,但這種工具的誤判率會很高——很多時候「同一條規則在多層被檢查」是刻意的防禦性設計(例如 API 邊界跟 Domain 邊界各自獨立驗證),不是所有重複都是壞味道。這裡機制能做的有限,最後還是要回到「這是不是同一件事」這個判斷,而判斷力是經驗,不是規則。
複雜度預算的門檻該設多少、架構測試的邊界該畫在哪裡,這些問題的答案不是憑空算出來的,而是要有人先對「這個系統的業務本質有多複雜」建立判斷,才能反推出一個合理的門檻。回到 Day 24 的討論,「留言比分類複雜」這件事,是我讀過需求之後的判斷,不是任何機制跑出來的結論。
這也解釋了一個容易被誤解的地方:這個系列講的「把期待寫成可執行規則」(第三部 Day 17-23),聽起來像是在說「把判斷力外包給規則」,但實際上規則只是判斷力的執行手臂,不是判斷力的替代品。沒有先做出正確判斷,規則寫得再精緻,驗證的也只是一個錯誤的門檻。
AI 沒有發明過度設計,它只是讓過度設計的速度追上了你按下 Enter 的速度;Review 要跟得上,審的就不能再是程式碼本身,而是產生程式碼的規則。 今天要補上一句但書:如果規則本身訂錯了,AI 一樣可以用同樣的速度,把一個訂錯的規則貫徹到底——機制驗證的效率不會分辨規則是對是錯,它只會讓錯的規則被更快速地執行。這不是反對這套方法論,而是提醒讀者:機制解決的是「執行落差」,不是「判斷落差」,兩種落差都存在,只有前者能被自動化。
你們團隊現有的 lint 規則、架構測試、CI 檢查清單裡,有沒有哪一條規則你已經記不清楚「當初為什麼要這樣訂」?如果有,這條規則現在還在保護什麼,還是已經變成一個沒人敢刪的化石?
Day 26 要換一個視角,用第一人稱誠實回顧我自己是怎麼從「相信人眼 review 就夠了」走到「相信可執行規則」這個立場的,包含過程中曾經有過的錯誤想法。
寫到這一篇,其實有點提醒我自己:這整個系列前面用了很多篇幅在講機制多有效,今天回頭誠實檢查一次,發現機制的效力永遠建立在「先有正確判斷」這個前提之上。這不是要推翻前面的內容,而是覺得,如果不把這條邊界講清楚,讀者很容易誤以為建好機制就萬事大吉,而那正是我最不想留給這個系列的印象。