技術債(Technical Debt)
是什麼?
技術債(Technical Debt) 是「用比喻借貸來換取短期開發速度」的概念。這個比喻最早由 Ward Cunningham(Wiki 與 Extreme Programming 的發明者之一)提出。他的原始定義是:
為了趕快交付,我們可以先寫出「不完全正確、但能動」的程式碼,就像借了一筆錢。只要我們之後回頭把它改對(還本金),借貸是健康的;但如果一直不還,利息會壓垮整個專案。
對應到比喻:
- 本金(Principal):那些不良的設計、混亂的程式碼、走捷徑留下的問題本身。
- 利息(Interest):因為這些問題還在,導致你日後每一次維護都多花的代價。
ℹ️關鍵:技術債不是「爛 code」的同義詞
Cunningham 的原意是「為了學習與速度而刻意、暫時地走捷徑」。重點在「比喻借貸」——你知道自己借了什麼、打算什麼時候還。後面我們會看到,「不知道自己寫爛了」其實是最危險的一種債。
身為資深 C# 開發者,你一定遇過這種情況:一個三年前「先這樣,之後再重構」的暫時方案,到今天還在 production 裡,而且每個新功能都要小心翼翼地繞過它。那段「之後」付出的所有額外時間,就是你一直在繳的利息。
技術債的「利息」是什麼
利息是技術債最容易被忽略、卻最致命的部分。它指的是:因為當初的爛設計,團隊在未來持續多付出的成本。 具體會以這些形式出現:
- 改一個小功能要花更久:因為邏輯糾纏、沒有測試保護,動一個地方要看十個地方。
- 更多的 bug:脆弱的設計讓修了 A 又冒出 B,回歸測試成本飆升。
- 更高的上線風險:沒人敢動的程式碼,每次部署都像拆炸彈。
- 新人上手變慢:看不懂、沒文件、命名混亂,教育訓練成本變高。
- 人員流失:長期在高利息的程式碼裡掙扎,工程師會疲乏甚至離職。
// 本金:一段為了趕上線寫死、又沒有抽象的計價邏輯
decimal price = quantity * 199; // 199 是什麼?沒人記得
// 利息:三個月後行銷要做「會員 9 折」,你發現這段邏輯
// 被複製貼上散落在 5 個地方,每個都要改、每個都可能漏改、每個都沒測試。
// 這多花的 2 小時 + 上線後漏改一處導致的 bug,就是你正在繳的利息。⚠️利息會「複利」滾動
技術債最可怕的地方在於利息會複利。爛 code 之上又長出新的爛 code(在錯誤的基礎上繼續開發),債務會以非線性的速度膨脹。等到「改不動了」才想處理,往往已經貴到只能整套重寫。
Fowler 技術債四象限
Martin Fowler 把技術債依「是否刻意(故意 vs 無意)」與「是否謹慎(謹慎 vs 魯莽)」分成四個象限。這是面試最常被問到的分類,務必理解清楚。
| 象限 | 心態(典型台詞) | 例子 | 評價 |
|---|---|---|---|
| 謹慎且故意 | 「我們知道這樣有債,但為了趕上市,先這樣,下個衝刺回來還。」 | 為了 Demo 把設定寫死,並開了還債的 issue 排進 backlog | 合理、健康的舉債 |
| 謹慎且無意 | 「現在我們才知道,當初應該這樣設計才對。」 | 學到新的領域知識後,發現舊架構不夠好 | 不可避免,是學習的自然結果 |
| 魯莽且故意 | 「沒時間做什麼設計啦,直接寫!」 | 明知該寫測試、該分層,卻刻意省略 | 危險,純粹用未來換現在 |
| 魯莽且無意 | 「分層架構?那是什麼?」 | 不知道有更好做法,寫出一團爛 code 還以為很正常 | 最危險:你連自己欠債都不知道 |
ℹ️重點:哪種債可接受?
謹慎且故意的債是合理的——你知道自己借了什麼、有意識地用它換時間,並計畫償還。最危險的是魯莽且無意:團隊缺乏能力或知識,寫出爛 code 卻渾然不覺,因此永遠不會去還,債務只會無聲地累積。面試時若被問「技術債都是壞事嗎」,答案就藏在這張表裡。
技術債的類型
技術債不只藏在程式碼裡,它散布在整個系統的各個層面:
- 程式碼債(Code Debt):重複貼上、命名混亂、超長方法、魔術數字、缺乏單一職責。
- 設計/架構債(Design/Architecture Debt):分層混亂、循環相依、上帝物件(God Object)、邊界不清,導致牽一髮動全身。
- 測試債(Test Debt):測試覆蓋率不足、被
[Ignore]掉的測試、只有「快樂路徑」沒有邊界案例。 - 文件債(Documentation Debt):沒有 README、API 文件過期、設計決策無人記錄。
- 相依套件債(Dependency Debt):使用過時、已停止維護或有安全漏洞的 NuGet 套件,且不敢升級。
- 基礎設施債(Infrastructure Debt):手動部署、沒有 CI/CD、伺服器 OS 過舊、環境設定靠口耳相傳。
簡單易懂的 C# 舉例
下面這段程式碼幾乎集滿了各種技術債,是現實專案裡很常見的樣子:
public class OrderService
{
public void PlaceOrder(string user, int qty)
{
// 設計/相依債:連線字串寫死在程式裡,環境一換就爆
var conn = new SqlConnection("Server=prod-db;User=sa;Password=1234;");
// 程式碼債:魔術數字,299 與 0.95 完全沒有語意
decimal total = qty * 299 * 0.95m;
// 程式碼債:到處複製貼上的驗證邏輯(這段在 5 個 Service 都有一份)
if (user == null || user.Length == 0)
throw new Exception("user error"); // 還丟了不具語意的例外
conn.Open();
// ... 直接拼字串組 SQL,連 SQL Injection 風險都一起借了
}
}public class OrderServiceTests
{
// 測試債:唯一一個會驗證金額的測試,被 Skip 掉了
[Ignore("計價邏輯改過,這個測試壞了,先關掉之後再修")]
[Test]
public void PlaceOrder_ShouldApplyDiscount() { /* ... */ }
}<!-- 相依債:三年沒升級、且已知有漏洞的套件,但「不敢升怕壞掉」 -->
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />每一行註解標出的,都是一筆正在滾利息的技術債。注意 [Ignore] 那個測試——它把「日後改計價邏輯沒人會發現出錯」的風險也一起借走了。
怎麼管理與償還
技術債本身不可怕,看不見、不還、或一次還太多才可怕。健康的做法是把它變成「看得見、可管理」的東西。
追蹤(讓債務可見)
- 在程式碼裡用
// TODO:或// HACK:標記,並串接到 issue。 - 為每筆值得處理的債開一張 issue/ticket,估出本金(要花多少時間還)。
- 建立技術債看板(Tech Debt Backlog),讓它和功能需求一起被排序、被看見。
償還策略
- 童子軍規則(Boy Scout Rule):「離開營地時,讓它比你來的時候更乾淨。」每次碰到一段程式碼,就順手把它改好一點點——重新命名一個變數、抽出一個方法、補一個測試。靠日積月累的小清理,避免債務累積。
- 預留還債時間:例如每個衝刺固定撥 10~20% 的產能還債,讓它成為常態而非例外。
- 邊改邊還(Refactor in context):當你正好要改某個功能時,順道重構它周邊的爛 code——因為你此刻最了解它,也最有理由動它。
💡童子軍規則的威力
你不需要排一個「三個月重構大專案」。每次 commit 都讓接觸到的程式碼乾淨一點點,整個 codebase 就會在不知不覺中持續變好。這是還技術債性價比最高的方式。
常見誤區
⚠️誤區一:把所有爛 code 都叫『技術債』
技術債特指「為了某種利益(速度、學習)而刻意或可理解地走的捷徑」。但有些 code 純粹是因為粗心、能力不足或不負責任而寫爛的——那不是「債」,那只是 bug 或低品質。把所有問題都包裝成「技術債」,會模糊責任,也讓人對它失去警覺。
⚠️誤區二:從不還債
最常見也最致命的誤區。團隊只顧著趕功能,技術債永遠排在 backlog 最底層,利息持續複利,直到系統「改不動」。記住:不還的債,最後會用「整個專案的速度」來償還。
⚠️誤區三:為了還債而大重寫(Big Rewrite)
另一個極端是「這 code 太爛了,全部砍掉重寫!」。大重寫風險極高——你會在重寫期間停止交付價值、重新踩一遍當年的坑、而且舊系統那些「醜陋但正確」的邊界處理常會被遺漏。多數情況下,漸進式重構(搭配測試保護)遠比大重寫安全。
實戰補充(Q&A)
Q:技術債一定是壞事嗎? A:不一定。就像貸款買房可以讓你提早住進去,技術債也能讓你提早上市、提早驗證商業假設。關鍵在於它是「謹慎且故意」的——你清楚知道借了什麼、利息多高、什麼時候還。真正的壞債是「魯莽且無意」:你根本不知道自己在欠債。
Q:什麼時候應該刻意舉債? A:當「時間的價值」高於「利息」時。例如:搶在競爭對手前推出 MVP、趕一個有截止日的行銷活動、或想先用簡單版本驗證使用者是否真的需要這功能(這時過度設計反而是浪費,呼應 YAGNI)。但前提是你要把這筆債記錄下來、排進 backlog,並在驗證成功後回頭償還。
Q:要怎麼跟主管/PM 溝通技術債? A:不要說「程式碼很醜」,要用「利息」的商業語言。例如:「這塊債讓我們每個相關需求多花 30% 工時,且上線風險高。花兩天還掉,之後的開發速度能恢復。」把技術問題翻譯成時間與風險,決策者才聽得進去。
理解測驗
🤔 根據 Martin Fowler 的技術債四象限,哪一種債通常被認為是「合理、健康」的?
🤔 在技術債的比喻中,「利息」最精確的對應是什麼?
🤔 關於「童子軍規則(Boy Scout Rule)」,下列敘述何者最正確?
重點整理
💡一句話記住
技術債就像刷信用卡:借「不良的程式碼」(本金)換「今天就能上線」的速度,代價是日後維護持續多付的「利息」——借得有計畫(謹慎且故意)並記得還是健康的,借了卻不知道、也從不還,才會把專案壓垮。
| 重點 | 一句話總結 |
|---|---|
| 原始定義 | Cunningham:用比喻借貸換取短期速度,本金=不良程式、利息=日後維護變慢 |
| 利息 | 因爛設計而日後多花的時間、bug、上線風險,會複利累積 |
| Fowler 四象限 | 謹慎且故意=合理舉債;魯莽且無意=最危險(不知道自己在欠債) |
| 債務類型 | 程式碼、設計/架構、測試、文件、相依套件、基礎設施 |
| 追蹤 | 用 TODO/issue/債務看板讓它「看得見、可排序」 |
| 償還 | 童子軍規則順手清、預留還債時間、邊改邊還 |
| 三大誤區 | 把所有爛 code 都叫技術債、從不還債、為還債而大重寫 |