如何打造無缺陷的系統?#

Brooks 在本章探討的核心問題是:如何設計一個沒有臭蟲的系統?而當系統確實有臭蟲時,又該如何有效地找到並消除它們?

他的答案分為三個層次:預防勝於治療元件除錯系統除錯

把臭蟲設計出去#

Brooks 認為,對抗臭蟲最有效的方式不是在事後找到它們,而是從一開始就讓它們無處藏身。

概念完整性#

第 4 到第 6 章反覆強調的概念完整性(conceptual integrity),是防止臭蟲的最重要因素。當系統的設計概念統一、介面一致,程式設計師就不容易因為誤解而寫出錯誤的程式碼。

測試規格書#

在撰寫任何程式碼之前,先測試規格書本身。方法很簡單:讓其他人嘗試僅憑規格書來實作功能。規格書中的模糊之處、矛盾和遺漏,會在這個過程中立即暴露。

規格書的測試不需要完整實作。讓不同的人獨立閱讀同一份規格書,然後比較他們的理解——差異之處就是規格書不夠清楚的地方。

由上而下的設計#

Brooks 引用 Niklaus Wirth 的方法論:由上而下的設計(top-down design)。過程如下:

  1. 從最高層的演算法開始,用粗略的步驟描述
  2. 將每個步驟進一步細化為更具體的子步驟
  3. 持續分解,直到每個步驟都足夠小、可以直接實作

這種方法確保在每個層次上,設計者都能清楚地看到整體結構,而不會迷失在細節中。

結構化程式設計#

Dijkstra 提出的結構化程式設計(structured programming)主張:所有程式邏輯都應該只使用三種控制結構——循序(sequence)、選擇(selection,即 if/then/else)、迴圈(iteration,即 while)。徹底避免任意的 GOTO 跳躍。

結構化程式設計的價值不僅僅在於「好看」。當程式只使用這三種結構時,其正確性可以被系統性地驗證。任意的 GOTO 則讓程式的執行路徑變得不可預測,大幅增加驗證的難度。

元件除錯#

當預防措施無法完全消除臭蟲時,就需要有效的除錯手段。Brooks 追溯了除錯技術的演進歷程:

  • 記憶體傾印(memory dump):最原始的方式,印出整段記憶體內容人工檢查
  • 快照(snapshot):在程式特定位置插入輸出語句,追蹤執行狀態
  • 互動式除錯(interactive debugging):透過終端機即時檢查和修改程式狀態

Brooks 引用 Gold 的研究指出,使用互動式除錯的程式設計師找到臭蟲的速度比傳統方式快 3 倍

然而,Brooks 也提醒:

互動式除錯工具再強大,也無法取代系統性的測試案例。隨意地「戳戳看」也許偶爾能碰巧找到問題,但有計畫的測試案例才能確保覆蓋率和可重複性。

系統除錯#

當個別元件都已通過測試,接下來便是最艱難的階段:將所有元件組合在一起,讓整個系統運作。

使用已除錯的元件#

這聽起來理所當然,卻經常被忽視。不要試圖同時除錯所有元件和整個系統——先確保每個元件獨立運作正常,再開始整合。

鷹架程式#

鷹架(scaffolding)是為了測試而建造的臨時支撐結構,包括:

  • 虛擬元件(stubs):模擬尚未完成的模組,回傳預設的測試資料
  • 微型測試檔案:小規模的輸入資料,便於觀察和驗證
  • 輔助記錄程式:額外的日誌輸出,追蹤系統運行時的內部狀態

鷹架程式的程式碼量可能高達產品程式碼的一半。這不是浪費——這是確保品質的必要投資。正如建造大樓時的鷹架,完工後會拆除,但建造過程中不可或缺。

控制變更#

使用第 12 章描述的程式庫系統,嚴格管理每一次變更。版本控制不是可有可無的奢侈品,而是系統除錯的基本前提。

一次只加一個元件#

漸進式整合(incremental integration)是系統除錯的黃金法則。每次只整合一個新元件,然後徹底測試。當新的臭蟲出現時,最大的嫌疑犯就是最近加入的那個元件。

量化更新#

以定義好的間隔釋出新版本,而非持續不斷地修改。這讓團隊能夠在一個穩定的基準上工作,也讓問題的定位更加容易。

本章重點#

  • 預防臭蟲比事後除錯更有效——概念完整性、規格書測試、由上而下設計、結構化程式設計
  • 互動式除錯比傳統方式快三倍,但仍需搭配系統性的測試案例
  • 鷹架程式(虛擬元件、測試資料、輔助日誌)是系統除錯的必要投資
  • 漸進式整合——一次只加一個元件——是定位問題的最有效策略
  • 版本控制和量化更新是維持系統穩定的基本紀律