複雜性透過三種主要徵兆顯現出來,每一種都會讓開發任務更加吃力。

變更放大(Change Amplification)#

看似單純的修改,卻得在許多不同地方改動程式碼。

範例:網站橫幅顏色

  • 舊做法:每個頁面各自寫死橫幅背景色 → 要改顏色,得手動修改每一頁;網站若有上千頁,幾乎不可能完成
  • 新做法:在中央位置統一定義一個共享變數,每頁參照該變數 → 一處修改,全站生效

Figure 2.1: 網站橫幅背景色——(a) 每頁分別寫死、(b) 集中變數共用、(c) 部分頁面額外指定強調色

好設計的目標之一是:降低每一個設計決策所影響的程式碼量

設計上的調整,不應牽動大量程式碼修改。

認知負擔(Cognitive Load)#

「認知負擔」指的是開發者完成某項任務所需掌握的知識量:

  • 認知負擔愈高,學習成本愈高
  • 漏掉重要資訊就會出 bug

範例:C 函式回傳指標

  • 函式配置記憶體、回傳指標,並要求呼叫端負責 free
  • 呼叫端若忘記 free 就造成記憶體洩漏(memory leak)
  • 若改成「同一個模組同時負責配置與釋放」,呼叫端就不必煩惱這件事

認知負擔可能來自:

  • API 方法太多
  • 全域變數
  • 不一致的設計
  • 模組間的依賴關係

不要用「行數」來衡量複雜性。

我看過某些框架,能讓應用程式只用幾行就寫出來,但要搞懂那幾行極其困難。有時候多幾行的寫法反而更簡單,因為它降低了認知負擔。

未知的未知(Unknown Unknowns)#

不清楚要修改哪些程式碼、也不知道為了完成任務需要哪些資訊。

範例:橫幅顏色加強版

  • 中央變數控制橫幅背景色(看似容易修改)
  • 但少數頁面用一個更深的顏色作為強調色,且該顏色寫死在頁面內
  • 改了 bannerBg 後,強調色得跟著改——但開發者多半不會意識到
  • 即使意識到了,也不容易找出哪些頁面用了強調色

三種徵兆中,未知的未知是最致命的

你需要某項知識,卻不知道自己需要它,也不知道從何得知;通常要等 bug 出現後才會知道。要保證萬無一失只能逐行讀完整個系統——這對任何規模的系統都是不可能的。

比較與目標#

徵兆嚴重度應對成本
變更放大知道要改哪 → 多花力氣即可完成
認知負擔知道該讀哪 → 變更仍可能正確
未知的未知不知該做什麼,連方案是否可行都不確定

好設計的重要目標之一是讓系統顯而易見(obvious)——這正是高認知負擔與未知的未知的反面。

顯而易見的系統允許開發者快速猜測該怎麼做,且有信心猜得對。第 18 章會討論讓程式碼變得更顯而易見的技巧。