本章探討技術債務(Technical Debt)的概念,涵蓋其定義、類型、成因、後果,以及在 Scrum 中如何管理技術債務的三大活動:控制累積、使其可見、以及償還債務。

技術債務概述#

Ward Cunningham 於 1992 年首次提出技術債務(Technical Debt)的隱喻:

首次發佈的程式碼就像是借債。少量的債務可以加速開發,只要及時透過重寫來償還。危險在於不還債——每一分鐘花在「不太對」的程式碼上,都是在為這筆債務付利息。

自 1990 年代初引入以來,業界對此定義已有所擴展。如今技術債務泛指所有影響軟體系統品質的問題,包括:

  • 不適當的設計——曾經合理但因業務或技術變遷已不再適用的設計
  • 已知缺陷——尚未修復的已知問題
  • 測試覆蓋不足——應該測試卻沒有測試的區域
  • 過度手動測試——本應自動化卻仍以手動方式進行的測試
  • 整合與發佈管理不良——耗時且容易出錯的流程
  • 平台經驗不足——例如缺乏有經驗的 COBOL 程式設計師

三種技術債務類型#

類型說明
天真債務(Naive Debt)因團隊或流程不成熟、缺乏訓練而產生的草率設計與差勁實作,本質上是魯莽且常常無意造成的
不可避免債務(Unavoidable Debt)不可預測且無法預防的債務,例如對良好設計的理解是在實作過程中逐漸浮現的,或第三方元件介面隨時間演進
策略性債務(Strategic Debt)刻意為達成重要短期目標(如搶先進入市場)而做出的有意識取捨

不論債務如何累積,技術債務隱喻的價值在於:它能讓業務人員迅速理解——就像財務債務一樣,技術債務也需要支付利息(額外的未來開發成本)。我們可以選擇繼續付利息(繞過問題工作),或償還本金(例如重構程式碼)。

技術債務的後果#

隨著技術債務水位升高,後果也愈加嚴重:

Figure 8.1: Consequences of technical debt

不可預測的臨界點#

技術債務以非線性方式增長。每新增一筆債務,可能造成遠超其表面大小的傷害。在某個「臨界質量」點,產品將變得不可管理——即使微小的變更也會引發巨大的不確定性。

交付時間增加#

債務越多,速率(Velocity)越低,交付新功能的時間反而增加。在競爭激烈的市場中,技術債務正積極地與我們的最佳利益作對。

缺陷數量顯著增加#

高技術債務使產品更加複雜,正確做事變得更難。複合性缺陷導致產品頻繁發生嚴重故障,管理大量缺陷消耗了開發新功能的時間。

開發與支援成本上升#

曾經簡單且便宜的事情,現在變得複雜且昂貴。

Figure 8.2: Cost-of-change curve affected by technical debt

當高技術債務曲線開始急劇攀升時,就已到達臨界質量的臨界點。成本上升還會改變功能開發的經濟性——在低債務下可行的功能,在高債務下可能變得太昂貴而不值得做。

其他後果#

後果說明
產品萎縮當我們停止新增功能或修復缺陷,產品變得越來越無吸引力
可預測性下降在高債務產品上做任何預測幾乎不可能
績效低落人們開始預期越來越低的開發績效,降低的期望在整個價值鏈中傳播
普遍性挫折開發的樂趣消失,團隊成員因倦怠而離開,士氣螺旋下降
客戶滿意度下降技術債務的傷害不僅限於開發團隊,更直接影響客戶體驗與信任

技術債務的成因#

面對截止日期的壓力#

策略性和天真性技術債務常常由商業壓力驅動。當實際速率低於計畫速率時,團隊面臨無法在期望日期前交付的現實。

Figure 8.3: Pressure to meet a deadline can lead to technical debt.

此時業務必須做出決定:削減範圍以符合期望日期,還是增加時間以可能的日期交付?不幸的是,許多情況下業務會要求團隊以所有功能在期望日期前完成——這迫使團隊加速,走捷徑而累積技術債務。

Figure 8.4: Accruing technical debt to meet unreasonable fixed scope and date

減少測試能加速速率是迷思#

一個普遍的迷思是:測試是額外開支,減少測試就能加快速度。

Figure 8.5: The myth, reality, and good practice of how testing affects velocity

  • 迷思:減少測試 → 增加債務 → 走得更快
  • 現實:減少測試 → 增加債務 → 走得更(問題延遲發現,修復成本更高)
  • 好的實踐:使用良好技術實踐(如 TDD)→ 降低債務 → 走得更快

債務疊加債務#

未來的技術債務會快速堆疊在既有債務之上。

Figure 8.6: As technical debt increases, velocity decreases.

如果這種模式持續下去,速率最終可能趨近於零——產品達到一種「恐懼修改」的狀態:一個小改動可能讓 18 個看似無關的地方崩壞。一旦陷入高技術債務,所有選擇都是壞選擇

  1. 什麼都不做,問題持續惡化
  2. 投入越來越多資源於技術債務縮減
  3. 宣布技術破產,以全新產品取代——承擔全額開發成本與風險
flowchart TD
    A[累積技術債務] --> B[速率下降]
    B --> C[更大壓力走捷徑]
    C --> A

    B --> D{速率趨近於零}
    D --> E[什麼都不做\n繼續受苦]
    D --> F[投入大量資源\n專門清理債務]
    D --> G[宣布技術破產\n從頭開始]

技術債務必須被管理#

技術債務如同財務債務,必須被管理。目標不是達到零債務(這可能在經濟上不合理),而是將技術債務維持在不會顯著影響未來產品開發的低水位

技術債務管理需要技術與業務雙方共同參與的平衡討論。這也是 Scrum 團隊中有 Product Owner 角色的原因之一。

Figure 8.7: Activities for managing technical debt

三大管理活動:

  1. 管理技術債務的累積(Managing the accrual)
  2. 使技術債務可見(Making technical debt visible)
  3. 償還技術債務(Servicing technical debt)

管理技術債務的累積#

使用良好的技術實踐#

首先要停止新增天真債務。成功的 Scrum 團隊通常採用以下實踐:

  • 簡單設計(Simple Design)
  • 測試驅動開發(TDD)
  • 持續整合(Continuous Integration)
  • 自動化測試(Automated Testing)
  • 重構(Refactoring)——在不改變外部行為的前提下重組程式碼內部結構,降低複雜度、提升可維護性

Cunningham 解釋重構的效益:客戶願意為新功能付費,但功能放不進去——先重組程式碼讓它放得進去,然後功能就容易實作了。這可以稱為「即時重構」(just-in-time refactoring)。

使用強健的完成定義#

在 Scrum 中,一個強健的完成定義(Definition of Done)能引導團隊在每個 Sprint 結束時達成低債務或零債務的結果。完成定義清單的技術涵蓋面越廣,累積技術債務的可能性就越低。

沒有強健的完成定義,就等於發放累積技術債務的許可證。

正確理解技術債務的經濟學#

要策略性地運用技術債務,必須正確理解其對決策經濟的影響。

Figure 8.8: Example technical debt economic analysis

範例分析(假設每月開發成本 $100K):

避免債務承擔債務
開發時間13 個月10 個月
開發成本$1.3M$1M
延遲成本$450K(3 個月銷售損失)$0
債務償還成本$0$400K(4 個月)
生命週期利潤成本$1.75M$1.4M + X + Y + Z

表面上看,承擔 $100K 技術債務換取 $450K 收入似乎划算。但常被忽略的因素包括:

  • 償還債務的延遲成本——償還時間導致其他產品或下一版本延遲
  • 多數組織不善於償還技術債務——當業務壓力到來,新功能開發總是優先於重工

做決定時,應偏向不承擔技術債務。多數組織大幅低估技術債務的真實成本,且在償還上遠不如自己預期的那麼勤勉。

使技術債務可見#

技術債務隱喻的主要好處之一是讓開發團隊和業務人員能在共享的脈絡下進行必要的對話。

在業務層面使技術債務可見#

開發人員通常對債務位置有概念,但業務人員通常缺乏對技術債務的可見性。想像如果能將技術債務列為公司資產負債表上的項目——就像財務債務一樣。

一些組織透過追蹤速率變化來呈現技術債務的業務影響。例如:固定成本每 Sprint $20K、歷史速率 20 點/Sprint(每點成本 $1K)。若因技術債務導致速率降至 10 點/Sprint,每點成本升至 $2K。200 點的工作從 $200K 變成 $400K。

在技術層面使技術債務可見#

Figure 8.9: Ways to make technical debt visible at the technical level

三種方式:

  1. 記錄在缺陷追蹤系統——利用現有工具,需加標籤以便區分
  2. 建立 Product Backlog 項目——讓重要技術債務與新功能同等可見,適用於償還成本高且需 Product Owner 參與排序的情況
  3. 建立專屬技術債務看板(Technical Debt Backlog)——以牆上的便利貼或卡片視覺化個別債務項目,通常放在 Sprint Backlog 旁邊

償還技術債務#

債務分類#

  • 偶遇型技術債務(Happened-upon)——在正常工作中偶然發現的未知債務
  • 已知技術債務(Known)——已被識別並透過某種方式使其可見的債務
  • 目標技術債務(Targeted)——已被指定在某個 Sprint 中償還的已知債務

償還演算法#

  1. 判斷已知技術債務是否應被償還(不是所有債務都該還)
  2. 在做其他工作時偶遇技術債務,就地清理至合理閾值;超出部分歸類為已知債務
  3. 每個 Sprint 考慮將部分已知債務指定為目標債務,優先償還與客戶價值工作交集的高利息債務

Figure 8.10: Approaches for servicing technical debt

並非所有技術債務都該償還#

以下情境中不需要償還技術債務:

  • 產品即將終結——對瀕臨退役的產品投入大量償還是不負責任的
  • 拋棄式原型(Throwaway Prototype)——價值在於驗證學習而非程式碼本身
  • 短生命週期產品——例如金融衍生品交易系統,產品只需存活三個月,在首個小時就產生了 $14M 收入

童子軍法則(Boy Scout Rule)#

「離開營地時,要比來時更乾淨。」

每次觸碰產品時,讓設計和實作稍微變好而非更差。團隊可以預留一定百分比的容量(實務上 5%~ 33%)來處理偶遇的技術債務。未能即時清理的偶遇債務應歸類為已知債務並使其可見。

優先償還高利息技術債務#

並非所有技術債務同等重要。例如:

  • 高利息:頻繁被修改、許多其他程式碼依賴、亟需重構的模組——我們持續為它付利息且利息不斷增加
  • 低利息:很少被使用也幾乎不被修改的程式碼中的已知問題——日常幾乎不付利息

如同財務債務,應先償還 18% 利率的債務,再處理 6% 利率的債務。

增量式償還技術債務#

避免「技術債務 Sprint」或「重構 Sprint」——這些看起來像是氣球式大額還款,表明債務累積時缺乏關注。應透過每個 Sprint 少量、持續地償還。

在執行客戶價值工作時同步償還#

這是最優雅的方式——在開發客戶價值功能的同時償還債務:

  1. 確保高品質工作,不新增天真債務
  2. 套用童子軍法則,清理偶遇的債務
  3. 針對性地償還工作區域中的目標技術債務

Figure 8.11: A technique for managing technical debt when using Scrum

在 Sprint Planning 時,將技術債務看板放在 Sprint Backlog 旁邊。當團隊選擇要做的 PBI 時,同時檢查是否與技術債務卡片相交。若有交集,就將該卡片拉入 Sprint Backlog,在完成功能開發的同時處理技術債務——這是一種簡單而優雅的方式,將技術債務償還與用戶價值創造對齊。