🎯 什麼情境該想到我
當你想「先湊合一下、之後再修」,或看到程式碼已經很亂而放任它繼續爛時。
⚙️ 怎麼用
一扇破窗(沒修的爛程式碼、爛設計、爛決策)會招來更多破窗。 一旦「反正已經很亂」的心態蔓延,系統會加速腐爛(軟體熵增)。
- 看到破窗就修,或至少擋起來:立刻修小問題,或加 TODO/工單追蹤,別放著不管。
- 維持乾淨的基準線:呼應童子軍規則(工具-註解是必要之惡 所屬的整潔紀律)——離開時比來時更乾淨。
- 別破第一扇窗:你自己也別為了趕而留下「反正只有一個」的爛攤子。
★ 為什麼「一扇破窗」會擴散:機制
這不是道德勸說,是團隊常規(norm)的形成機制:
- 工程師寫新程式碼時,會參考周圍的程式碼決定「這裡的標準是什麼」。
- 周圍都是乾淨的 → 「我也該寫乾淨」;周圍有 3 個
// TODO: fix later和 1 個被註解掉的區塊 → 「這裡的標準是能跑就好」。 - 每個人都做出局部合理的決定,整體品質就單向往下。
所以破窗的代價不是那一扇窗本身,而是它改變了下一個人對標準的判斷。這也是為什麼修小問題的回報比看起來大。
★ 讓「不再破窗」自動化:棘輪(ratchet)
對舊專案,「一次修好所有問題」不可能;但「不准變得更糟」可以做到,而且可以用 CI 強制:
| 做法 | 怎麼實作 |
|---|---|
| 只檢查新程式碼 | golangci-lint 的 --new-from-rev=origin/main:只回報這次變更新增的問題,舊問題先不管 |
| 數字只能降不能升 | 把目前的警告數/覆蓋率存進 repo,CI 比對:警告數 > 基準 → 失敗;< 基準 → 更新基準 |
| 碰到哪修到哪 | 童子軍規則:你改的那個函式,離開時比進來時乾淨一點(改個名、刪個死程式碼) |
| 明確標記技術債 | // TODO(#482) 必須帶 issue 編號;沒編號的 TODO 在 review 擋下 |
棘輪的好處:不需要排一個「清理 sprint」,品質靠日常的每個 PR 單調往上爬。
★ 哪些「破窗」要立刻修,哪些可以登記
| 立刻修(修的成本 < 5 分鐘 或 會擴散) | 登記成債(修的成本高、不會擴散) |
|---|---|
| 編譯警告、lint 警告 | 模組邊界畫錯 |
| 被註解掉的程式碼 | 缺整合測試的舊模組 |
| 誤導的名字、過時的註解 | 需要資料遷移的 schema 問題 |
| 不穩定(flaky)的測試——它教會大家「紅燈可以忽略」 | 效能問題(有量測數據佐證的) |
flaky test 是最危險的破窗:一旦團隊習慣「CI 紅了就重跑」,真正的紅燈也會被重跑掉。發現就要隔離(標記跳過並開單)或修掉,不能放著。
🧪 我實際套用的紀錄
- 2026-07-15:(待填)
⚠️ 注意
-
緊急時可暫時妥協,但要明確記錄為技術債並排程償還,不能無聲無息地累積。
-
破窗理論在犯罪學上的證據有爭議(原始的都市治安政策效果至今仍有爭論);《程式設計師修煉之道》借用的是它的比喻,而軟體團隊中的常規擴散,靠 code review 和 CI 的經驗就能觀察到,不依賴那份犯罪學結論。
-
別把「修破窗」變成拒絕上線的藉口:目標是不讓品質單向下滑,不是每次上線前把整個 repo 擦亮。
🔗 相關工具
- 工具-程式碼壞味道 —— 破窗的具體長相,用清單認出「這裡已經破了」
- 工具-小步重構 —— 補窗的手法,一次補一塊比等大重構務實
- 工具-區分修復與掩蓋症狀 —— 品質關卡,用膠帶貼住破窗不算修,只是讓它更難看見