iT邦幫忙

2026 iThome 鐵人賽

DAY 5
0
Software Development

當 AI 寫得比你讀得快:Code Review 該審什麼系列 第 5

Day 5:意外複雜度 vs 本質複雜度——怎麼分辨程式碼的複雜度長在哪裡

  • 分享至 

  • xImage
  •  

前言:「這系統本來就很複雜啊,業務邏輯本來就多」

每次質疑一份程式碼是不是過度設計,很容易得到這句回應:業務本身就複雜,程式碼複雜是應該的。這句話有時候是對的——真的有些領域的規則多、例外多,程式碼複雜度反映的是問題本身的複雜度。但這句話也常常是一個擋箭牌,把「我們自己加上去的複雜度」包裝成「業務本來就這樣」。

今天要講的,就是怎麼把這兩種複雜度分開看:業務問題本身固有的難度,跟我們在解決問題過程中自己製造出來的難度,是兩件不同的事,混在一起看,你永遠沒辦法判斷一份程式碼是不是過度設計。

今日目標

  • 分清楚「本質複雜度」(essential complexity)跟「意外複雜度」(accidental complexity)這兩個概念
  • 學會用一個簡單的檢查方式:業務規則數量有沒有跟程式碼複雜度成比例
  • ai-news-test 的實際數字驗證這個判斷方式
  • 認識「看起來合理的架構模式」也可能是意外複雜度的偽裝
  • 建立一個 review 時可以直接拿來用的提問句

兩種複雜度的定義

這組概念最早由 Fred Brooks 在討論軟體工程困境時提出:

  • 本質複雜度:問題領域本身固有的困難,不管用什麼工具、什麼架構,都無法消除,只能想辦法管理。例如「已發佈的文章要先下架才能編輯」這條規則本身帶來的狀態轉換邏輯,是新聞發佈這個問題領域的一部分。
  • 意外複雜度:因為工具選擇、架構決策、過度設計而額外產生的困難,理論上可以透過更好的做法消除或減少。例如同一條「標題不可為空」規則被檢查三次、用四層 Repository 裝飾器包裝一個從沒被真正用到的重試邏輯——這些困難跟業務問題本身無關,是實作過程中自己加上去的。

過度設計的本質,就是把意外複雜度包裝成「架構完整」的樣子,讓它看起來像是本質複雜度的一部分,不容易被質疑。

用業務規則數量當基準線

一個簡單但好用的檢查方式:先數清楚這個系統的本質複雜度有多少(業務規則有幾條、狀態轉換有幾種),再回頭看程式碼複雜度是不是跟這個數字成比例。

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 倍,差的部分幾乎全是意外複雜度
  • 架構模式(CQRS、Repository、Specification)的存在,不能拿來當本質複雜度的證據——模式可以套在任何複雜度的問題上
  • 過度設計最擅長把意外複雜度包裝成看起來像本質複雜度的樣子
  • 建議的檢查順序:先確認業務規則的本質複雜度,再回頭檢查程式碼複雜度有沒有超出比例

明日預告

Day 6 要把「複雜度跟業務規則成不成比例」這個判斷方式,收斂成一個更具體、更容易操作的量化指標:改動範圍 vs 需求範圍。

老派工程師的心得

我以前很容易被「這系統設計得很完整」這種說法說服,尤其是自己沒有時間逐行讀完全部程式碼的時候,看到清楚的分層、熟悉的模式名稱,會下意識覺得「這應該是有經驗的人寫的」。後來吃過幾次虧才學會反過來想:複雜不等於嚴謹,有時候只是把簡單的問題包裝得比較複雜而已。 分清楚本質複雜度跟意外複雜度,某種程度上是在提醒自己不要被「看起來很懂」這件事唬住——不管寫的人是資深工程師,還是一個 AI coding agent。


上一篇
Day 4:過度設計不是 AI 帶來的新病——簡短技術史回顧
下一篇
Day 6:「改動範圍 vs 需求範圍」——一個可以量化過度設計的指標
系列文
當 AI 寫得比你讀得快:Code Review 該審什麼6
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言