每次質疑一份程式碼是不是過度設計,很容易得到這句回應:業務本身就複雜,程式碼複雜是應該的。這句話有時候是對的——真的有些領域的規則多、例外多,程式碼複雜度反映的是問題本身的複雜度。但這句話也常常是一個擋箭牌,把「我們自己加上去的複雜度」包裝成「業務本來就這樣」。
今天要講的,就是怎麼把這兩種複雜度分開看:業務問題本身固有的難度,跟我們在解決問題過程中自己製造出來的難度,是兩件不同的事,混在一起看,你永遠沒辦法判斷一份程式碼是不是過度設計。
ai-news-test 的實際數字驗證這個判斷方式這組概念最早由 Fred Brooks 在討論軟體工程困境時提出:
過度設計的本質,就是把意外複雜度包裝成「架構完整」的樣子,讓它看起來像是本質複雜度的一部分,不容易被質疑。
一個簡單但好用的檢查方式:先數清楚這個系統的本質複雜度有多少(業務規則有幾條、狀態轉換有幾種),再回頭看程式碼複雜度是不是跟這個數字成比例。
ai-news-test 這個案例很適合拿來驗證這個判斷方式,因為它的業務規則數量刻意設計得很小、很好數:
| 版本 | 業務規則數量 | 檔案數 | 總行數 |
|---|---|---|---|
| 乾淨版本 | 4 條 | 3 | 249 |
| 過度設計版本 | 4 條(完全相同) | 745 | 21,727 |
本質複雜度完全沒變——同樣 4 條規則(標題不可為空、不可重複發佈、已發佈需先下架才能編輯、其餘為基本 CRUD)。但程式碼複雜度差了 87 倍。這 87 倍裡,幾乎全部都是意外複雜度:過度設計版本裡真正被測試呼叫到、對應這 4 條業務規則實際邏輯的程式碼只有 62 個檔案、1,738 行;其餘 683 個檔案、約 92% 的程式碼,是跟這 4 條業務規則完全無關的假設性功能與欄位。
這是最容易讓人上當的地方。看到程式碼裡有 CQRS 的 Command/Query 分離、有 Specification Pattern、有多層 Repository 裝飾器,直覺會覺得「這系統的業務邏輯應該蠻複雜的,才需要用到這些模式」。但模式本身不會證明它有存在的必要性——任何架構模式都可以套用在任何複雜度的問題上,套用了不代表這個複雜度是問題本身需要的。
❌ 把架構模式的存在,當成本質複雜度的證據:
「這系統用了 4 層 Repository 裝飾器(Auditing / Metrics /
RateLimiting / CircuitBreaker),業務邏輯應該很複雜。」
實際檢查:ai-news-test 過度設計版本裡的這幾種裝飾器變體,沒有一層真的被組進實際呼叫鏈——它們存在,但從未被執行路徑用到。這就是意外複雜度偽裝成本質複雜度最典型的樣子:看起來像是為了應付複雜業務而存在的架構,實際上只是被建起來,卻從未真正參與運作。
✅ 正確的檢查順序應該反過來:先確認業務規則本身有沒有這個複雜度的需求(例如真的需要跨系統一致性保證、真的有多個資料來源需要容錯),再回頭看架構模式是不是為了滿足這個已確認存在的需求。
如果 review 時分不清楚一段複雜的程式碼是「本質複雜度」還是「意外複雜度」,review 者很容易被架構的完整度唬住,誤以為複雜就是合理的。這正是過度設計最容易通過 review 的原因——它披著「專業架構」的外衣,讓人不敢輕易質疑,好像質疑它就是不懂軟體設計。
反過來說,一旦養成「先確認本質複雜度有多少」的習慣,就有了一個相對客觀的基準線,可以拿來檢查程式碼複雜度有沒有超出這個基準太多。這也呼應本系列的主題句:AI 沒有發明過度設計,它只是讓過度設計的速度追上了你按下 Enter 的速度;Review 要跟得上,審的就不能再是程式碼本身,而是產生程式碼的規則。 這裡的「規則」不能只是「這段程式碼看起來合不合理」的主觀感受,而要有一個可以量化、可以對照的基準。
拿你手上正在維護的一個模組,試著先列出它真正的業務規則有幾條,再回頭數一下有多少檔案、多少類別在服務這些規則。這個比例,跟你原本的直覺印象一樣嗎?
ai-news-test 案例:業務規則數量完全不變(4 條),程式碼複雜度卻能差到 87 倍,差的部分幾乎全是意外複雜度Day 6 要把「複雜度跟業務規則成不成比例」這個判斷方式,收斂成一個更具體、更容易操作的量化指標:改動範圍 vs 需求範圍。
我以前很容易被「這系統設計得很完整」這種說法說服,尤其是自己沒有時間逐行讀完全部程式碼的時候,看到清楚的分層、熟悉的模式名稱,會下意識覺得「這應該是有經驗的人寫的」。後來吃過幾次虧才學會反過來想:複雜不等於嚴謹,有時候只是把簡單的問題包裝得比較複雜而已。 分清楚本質複雜度跟意外複雜度,某種程度上是在提醒自己不要被「看起來很懂」這件事唬住——不管寫的人是資深工程師,還是一個 AI coding agent。