複雜性透過三種主要徵兆顯現出來,每一種都會讓開發任務更加吃力。
變更放大(Change Amplification)#
看似單純的修改,卻得在許多不同地方改動程式碼。
範例:網站橫幅顏色
- 舊做法:每個頁面各自寫死橫幅背景色 → 要改顏色,得手動修改每一頁;網站若有上千頁,幾乎不可能完成
- 新做法:在中央位置統一定義一個共享變數,每頁參照該變數 → 一處修改,全站生效

Figure 2.1: 網站橫幅背景色——(a) 每頁分別寫死、(b) 集中變數共用、(c) 部分頁面額外指定強調色
好設計的目標之一是:降低每一個設計決策所影響的程式碼量。
設計上的調整,不應牽動大量程式碼修改。
認知負擔(Cognitive Load)#
「認知負擔」指的是開發者完成某項任務所需掌握的知識量:
- 認知負擔愈高,學習成本愈高
- 漏掉重要資訊就會出 bug
範例:C 函式回傳指標
- 函式配置記憶體、回傳指標,並要求呼叫端負責
free - 呼叫端若忘記
free就造成記憶體洩漏(memory leak) - 若改成「同一個模組同時負責配置與釋放」,呼叫端就不必煩惱這件事
認知負擔可能來自:
- API 方法太多
- 全域變數
- 不一致的設計
- 模組間的依賴關係
不要用「行數」來衡量複雜性。
我看過某些框架,能讓應用程式只用幾行就寫出來,但要搞懂那幾行極其困難。有時候多幾行的寫法反而更簡單,因為它降低了認知負擔。
未知的未知(Unknown Unknowns)#
不清楚要修改哪些程式碼、也不知道為了完成任務需要哪些資訊。
範例:橫幅顏色加強版
- 中央變數控制橫幅背景色(看似容易修改)
- 但少數頁面用一個更深的顏色作為強調色,且該顏色寫死在頁面內
- 改了
bannerBg後,強調色得跟著改——但開發者多半不會意識到 - 即使意識到了,也不容易找出哪些頁面用了強調色
三種徵兆中,未知的未知是最致命的。
你需要某項知識,卻不知道自己需要它,也不知道從何得知;通常要等 bug 出現後才會知道。要保證萬無一失只能逐行讀完整個系統——這對任何規模的系統都是不可能的。
比較與目標#
| 徵兆 | 嚴重度 | 應對成本 |
|---|---|---|
| 變更放大 | 中 | 知道要改哪 → 多花力氣即可完成 |
| 認知負擔 | 中 | 知道該讀哪 → 變更仍可能正確 |
| 未知的未知 | 高 | 不知該做什麼,連方案是否可行都不確定 |
好設計的重要目標之一是讓系統顯而易見(obvious)——這正是高認知負擔與未知的未知的反面。
顯而易見的系統允許開發者快速猜測該怎麼做,且有信心猜得對。第 18 章會討論讓程式碼變得更顯而易見的技巧。