本章探討 Agile 的技術實踐,包含 TDD、Refactoring、Simple Design 與 Pair Programming。作者強調,這些技術實踐是 Agile 的核心,缺少任何一項都會讓 Agile 淪為空殼。許多團隊嘗試跳過技術實踐來實施 Agile,結果無一不以失敗告終。

Test-Driven Development#

複式簿記的類比#

作者將 TDD 與會計的 複式簿記(Double-Entry Bookkeeping) 做類比。會計師在帳簿中將每筆交易記錄兩次——一次記為借方、一次記為貸方——最終在資產負債表上兩者相減必須為零,藉此偵測錯誤。

TDD 的運作方式完全類似:每一個預期行為都被記錄兩次——一次以 測試 的形式,一次以 正式程式碼 的形式。兩者一起執行時,結果應為「零測試失敗」。就像會計師被教導逐筆記帳、每次都檢查餘額一樣,TDD 開發者也被教導每次只加入一個行為,先寫失敗的測試,再寫通過的程式碼。

TDD 的三條規則#

TDD 可以歸結為三條簡單的規則:

  1. 不寫任何正式程式碼,直到你先寫了一個因缺少該程式碼而失敗的測試
  2. 測試只寫到剛好失敗的程度——編譯失敗也算失敗
  3. 正式程式碼只寫到剛好讓當前失敗測試通過的程度

這三條規則意味著開發者在測試與正式程式碼之間以極短的週期(大約數秒鐘)來回切換。結果是:在任意時間點,團隊中任何一位開發者手上的程式碼,距離上次全部測試通過不會超過一分鐘。

TDD 帶來的效益#

  • 減少除錯時間:如果所有程式碼一分鐘前都還能正常運作,那麼任何新出現的錯誤都只有不到一分鐘的歷史,通常不需要動用 debugger 就能找到問題。熟練使用 debugger 反而代表你花了太多時間在除錯上。
  • 活文件(Living Documentation):遵循三條規則所產生的測試,本身就是整個系統最好的文件。它們用開發者熟悉的語言撰寫、絕對明確、可以執行、且不會與程式碼脫節。每個測試都是獨立的,描述系統某一小部分的行為。
  • 樂趣:事後補寫測試枯燥無味,因為你已經知道程式碼能動。但先寫測試時,每個測試都是一個小挑戰,每次讓測試通過都是一次小勝利。
  • 完整性:事後補寫測試時,遇到難以測試的程式碼,開發者往往會跳過,留下測試套件的漏洞。先寫測試則不會有這個問題——每一行正式程式碼都是為了讓某個測試通過而寫的,因此測試覆蓋率自然很高。
  • 驅動設計(Design):先寫測試會迫使你把程式碼設計成容易測試的結構,而 可測試性(testability)本質上就是解耦(decoupling)。因此 TDD 常被稱為一種設計技術。
  • 勇氣(Courage):這是 TDD 最重要的效益。當你擁有一套完整且可信賴的測試套件時,你不再害怕修改程式碼、不再害怕清理混亂。沒有這種勇氣,程式碼只會持續腐化,最終變成無法維護的義大利麵,團隊生產力指數下降,直到不得不全面重寫。

測試覆蓋率是 團隊指標,不是管理指標。管理者不應將覆蓋率當作目標或 KPI,否則開發者會為了衝數字而減少測試中的斷言。覆蓋率的解讀需要對程式碼與測試有深入了解,應由團隊自行用來調整測試策略。

不要 因為覆蓋率不足就讓建置失敗。這樣做只會逼迫開發者移除斷言來拉高數字,適得其反。

Refactoring#

Refactoring 是在不改變行為(以測試定義)的前提下,改善程式碼結構的實踐。這與 TDD 緊密耦合——要無畏地重構,你需要一套高信心的測試套件。

重構的範圍涵蓋很廣:

  • 簡單的:重新命名變數、拆分大函式為小函式
  • 進階的:將 switch 語句改為多型分派、反轉依賴、跨架構邊界移動模組

Red/Green/Refactor 循環#

重構與 TDD 的三條規則交織在一起,形成 Red/Green/Refactor 循環:

  1. Red:寫一個失敗的測試
  2. Green:用最簡單的方式讓測試通過
  3. Refactor:清理剛才寫的程式碼
  4. 回到步驟 1

Figure 5.1: The Red/Green/Refactor cycle

flowchart LR
    R["Red<br>寫失敗測試"] --> G["Green<br>讓測試通過"]
    G --> F["Refactor<br>清理程式碼"]
    F --> R

核心觀念是:讓程式碼能動讓程式碼乾淨 是兩個獨立的維度。同時追求兩者太困難,所以我們先用任何方式讓它能動(Green),再回頭清理(Refactor)。

Refactoring 是持續進行的,不是排程活動。不應該在計畫中看到「重構」這個工作項目。我們不會連續好幾天製造混亂再一次清理,而是每隔一兩分鐘就清理剛才的小混亂。

大規模重構#

有時需求變化會讓你意識到目前的設計或架構不再理想。這類大規模的結構調整仍然在 Red/Green/Refactor 循環中進行——不為此另開專案或預留時間,而是在日常開發中一小步一小步地遷移。這個過程可能跨越數天、數週甚至數月,期間系統始終保持所有測試通過,隨時可以部署到生產環境。

Simple Design#

Simple Design 是 Refactoring 的目標之一。其核心是:只寫必要的程式碼,並保持最簡單、最小、最具表達力的結構。

Kent Beck 提出的四條 Simple Design 規則(按優先順序排列):

  1. 通過所有測試——程式碼必須能正常運作
  2. 揭示意圖(Reveal the Intent)——程式碼應該容易閱讀、自我描述,清楚表達開發者的意圖
  3. 消除重複(Remove Duplication)——不讓程式碼重複表達相同的事情,可能需要使用 Design Patterns(如 Template Method、Strategy、Decorator、Visitor)
  4. 減少元素(Decrease Elements)——在消除重複後,盡量減少類別、函式、變數等結構元素的數量

Design Weight#

Design Weight(設計重量) 是指設計複雜度對開發者造成的認知負擔。設計越複雜,開發者理解和操作系統所需的時間和精力就越大。

然而,適度增加設計複雜度可以 降低 需求複雜度帶來的負擔——兩者不是簡單相加的關係。Simple Design 的目標就是在設計複雜度與需求複雜度之間找到平衡點,讓整體系統複雜度最小化,從而最大化生產力。

Pair Programming#

配對的本質#

Pair Programming 是兩人(或更多人)共同解決同一個程式問題的實踐。關於配對的幾個關鍵原則:

原則說明
自願參與沒有人應該被強迫配對
間歇進行團隊大約 50% 的時間配對(30%~80% 都合理),其餘時間獨立工作
自然形成配對不應由管理者排程,而是由開發者自行決定何時與誰配對
短期合作一次配對可能持續 15 分鐘到一天不等,通常是一兩個小時

配對的形式#

形式說明
Driver / Navigator一人操控鍵盤滑鼠(Driver),另一人從更高層次審視(Navigator)
Ping-Pong一人寫測試,另一人讓測試通過並寫下一個測試
共同創作最常見的形式,兩人平等地共享鍵盤滑鼠,沒有固定角色

配對可以在同一台工作站進行,也可以透過螢幕共享軟體遠端進行。

配對的價值#

價值說明
知識分享配對是防止知識孤島(Knowledge Silo)形成的最佳方式,確保團隊中沒有人是不可替代的
品質提升多一雙眼睛審視程式碼,通常能減少錯誤、改善設計品質
取代 Code Review配對本身就是一種動態的 Code Review,不只是靜態檢查編碼規範,而是在創作過程中即時審視程式碼的現狀與未來方向

資深開發者應多與資淺開發者配對,專精特定領域的開發者應多與其他領域的人配對。目標是 散播和交換知識,而非集中知識。

成本考量#

研究顯示配對的直接成本約為 15%——意即 115 位配對的開發者大約等於 100 位獨立工作(不含 Code Review)的開發者。若團隊 50% 時間配對,生產力損失不到 8%。但如果配對取代了 Code Review,生產力可能完全不受影響。再加上交叉訓練與深度協作帶來的無形效益,整體而言配對通常是划算的。

Mob Programming#

配對不限於兩人。有時三人、四人甚至更多人會一起解決某個問題,這稱為 Mob Programming

永遠不要向管理者請求許可才去配對、寫測試或重構。你是專家,這些是你的專業判斷,應由你自己決定。

Conclusion#

技術實踐是任何 Agile 實施中最關鍵的成分。沒有技術實踐的 Agile 注定失敗,因為 Agile 本身就是一種能快速產出大量成果的高效機制——如果不以技術實踐來維持技術品質,團隊的生產力將迅速衰退,陷入不可逆轉的死亡螺旋。