系統性除錯
概述
隨機修復浪費時間並產生新錯誤。快速補丁掩蓋潛在問題。
核心原則: 始終在嘗試修復之前找到根本原因。症狀修復是失敗。
違反這個流程的字面意思就是違反除錯的精神。
鐵律
code
沒有根本原因調查就沒有修復
如果你沒有完成第一階段,你不能提出修復。
何時使用
用於任何技術問題:
- •測試失敗
- •生產中的錯誤
- •意外行為
- •效能問題
- •建置失敗
- •整合問題
特別在以下情況使用:
- •時間壓力下(緊急情況使猜測變得誘人)
- •"只要一個快速修復"看起來很明顯
- •你已經嘗試了多個修復
- •之前的修復沒有起作用
- •你不完全理解問題
不要跳過當:
- •問題看起來很簡單(簡單的錯誤也有根本原因)
- •你很趕時間(匆忙保證返工)
- •經理現在就要修好(系統性比胡亂嘗試更快)
四個階段
你必須完成每個階段才能進入下一個。
第一階段:根本原因調查
在嘗試任何修復之前:
- •
仔細閱讀錯誤訊息
- •不要跳過錯誤或警告
- •它們通常包含確切的解決方案
- •完整閱讀堆疊追蹤
- •記錄行號、檔案路徑、錯誤代碼
- •
一致性重現
- •你能可靠地觸發它嗎?
- •確切的步驟是什麼?
- •每次都會發生嗎?
- •如果無法重現 → 收集更多資料,不要猜測
- •
檢查最近的變更
- •什麼變更可能導致這個?
- •Git diff,最近的提交
- •新的依賴項,配置變更
- •環境差異
- •
在多組件系統中收集證據
當系統有多個組件(CI → 建置 → 簽名,API → 服務 → 資料庫)時:
在提出修復之前,新增診斷儀器:
code對於每個組件邊界: - 記錄什麼資料進入組件 - 記錄什麼資料離開組件 - 驗證環境/配置傳播 - 檢查每層的狀態 執行一次以收集證據顯示哪裡壞了 然後分析證據以識別失敗的組件 然後調查那個特定組件
範例(多層系統):
bash# 第 1 層:工作流程 echo "=== 工作流程中可用的秘密:===" echo "IDENTITY: ${IDENTITY:+SET}${IDENTITY:-UNSET}" # 第 2 層:建置腳本 echo "=== 建置腳本中的環境變數:===" env | grep IDENTITY || echo "IDENTITY 不在環境中" # 第 3 層:簽名腳本 echo "=== 鑰匙圈狀態:===" security list-keychains security find-identity -v # 第 4 層:實際簽名 codesign --sign "$IDENTITY" --verbose=4 "$APP"這揭示了: 哪一層失敗(秘密 → 工作流程 ✓,工作流程 → 建置 ✗)
- •
追蹤資料流
當錯誤在呼叫堆疊深處時:
參見此目錄中的
root-cause-tracing.md以了解完整的向後追蹤技術。快速版本:
- •壞值從哪裡產生?
- •什麼用壞值呼叫了這個?
- •繼續向上追蹤直到找到源頭
- •在源頭修復,而不是在症狀處
第二階段:模式分析
在修復之前找到模式:
- •
找到工作範例
- •在同一程式碼庫中找到類似的工作程式碼
- •什麼類似的東西能工作而什麼壞了?
- •
與參考比較
- •如果實作模式,完整閱讀參考實作
- •不要略讀 - 閱讀每一行
- •在應用之前完全理解模式
- •
識別差異
- •工作和壞掉之間有什麼不同?
- •列出每個差異,無論多小
- •不要假設"那不可能有關係"
- •
理解依賴
- •這需要什麼其他組件?
- •什麼設定、配置、環境?
- •它做了什麼假設?
第三階段:假設和測試
科學方法:
- •
形成單一假設
- •清楚陳述:"我認為 X 是根本原因因為 Y"
- •寫下來
- •具體,不要模糊
- •
最小化測試
- •做最小的可能變更來測試假設
- •一次一個變數
- •不要一次修復多件事
- •
繼續前驗證
- •它起作用了嗎?是 → 第四階段
- •沒有起作用?形成新假設
- •不要在上面添加更多修復
- •
當你不知道時
- •說"我不理解 X"
- •不要假裝知道
- •尋求幫助
- •更多研究
第四階段:實作
修復根本原因,不是症狀:
- •
建立失敗測試案例
- •最簡單的可能重現
- •如果可能的話自動化測試
- •如果沒有框架則一次性測試腳本
- •修復前必須有
- •使用
hi-skills:test-driven-development技能來編寫正確的失敗測試
- •
實作單一修復
- •解決識別出的根本原因
- •一次一個變更
- •沒有"既然在這裡"的改進
- •沒有捆綁的重構
- •
驗證修復
- •測試現在通過了嗎?
- •沒有其他測試壞了?
- •問題真的解決了?
- •
如果修復不起作用
- •停止
- •計數:你嘗試了多少修復?
- •如果 < 3:返回第一階段,用新資訊重新分析
- •如果 ≥ 3:停止並質疑架構(下面的步驟 5)
- •在沒有架構討論的情況下不要嘗試第 4 次修復
- •
如果 3+ 次修復失敗:質疑架構
表明架構問題的模式:
- •每次修復揭示新的共享狀態/耦合/不同位置的問題
- •修復需要"大規模重構"才能實作
- •每次修復在其他地方產生新症狀
停止並質疑基本面:
- •這個模式根本上是合理的嗎?
- •我們是否"純粹出於慣性堅持它"?
- •我們應該重構架構 vs 繼續修復症狀?
在嘗試更多修復之前與你的人類夥伴討論
這不是失敗的假設 - 這是錯誤的架構。
危險信號 - 停止並遵循流程
如果你發現自己在想:
- •"現在先快速修復,稍後調查"
- •"就試試改變 X 看看是否有效"
- •"添加多個變更,執行測試"
- •"跳過測試,我會手動驗證"
- •"可能是 X,讓我修復那個"
- •"我不完全理解但這可能有效"
- •"模式說 X 但我會用不同方式調整它"
- •"這裡是主要問題:[在沒有調查的情況下列出修復]"
- •在追蹤資料流之前提出解決方案
- •"再嘗試一次修復"(當已經嘗試了 2+ 次時)
- •每次修復揭示不同位置的新問題
所有這些意味著:停止。返回第一階段。
如果 3+ 次修復失敗: 質疑架構(參見第 4.5 階段)
你的人類夥伴表示你做錯的信號
注意這些重定向:
- •"那沒有發生?" - 你假設而沒有驗證
- •"它會顯示我們...?" - 你應該添加證據收集
- •"停止猜測" - 你在沒有理解的情況下提出修復
- •"超級思考這個" - 質疑基本面,而不只是症狀
- •"我們卡住了?"(沮喪)- 你的方法沒有起作用
當你看到這些時: 停止。返回第一階段。
常見合理化
| 藉口 | 現實 |
|---|---|
| "問題很簡單,不需要流程" | 簡單問題也有根本原因。流程對簡單錯誤很快。 |
| "緊急情況,沒時間走流程" | 系統性除錯比猜測和反覆嘗試更快。 |
| "先試試這個,然後調查" | 第一次修復設定模式。從一開始就做對。 |
| "確認修復有效後我會寫測試" | 未測試的修復不會持久。先測試證明它。 |
| "一次多個修復節省時間" | 無法隔離什麼有效。導致新錯誤。 |
| "參考太長了,我會調整模式" | 部分理解保證錯誤。完整閱讀它。 |
| "我看到問題了,讓我修復它" | 看到症狀 ≠ 理解根本原因。 |
| "再嘗試一次修復"(在 2+ 次失敗後) | 3+ 次失敗 = 架構問題。質疑模式,不要再次修復。 |
快速參考
| 階段 | 關鍵活動 | 成功標準 |
|---|---|---|
| 1. 根本原因 | 閱讀錯誤,重現,檢查變更,收集證據 | 理解什麼和為什麼 |
| 2. 模式 | 找到工作範例,比較 | 識別差異 |
| 3. 假設 | 形成理論,最小化測試 | 確認或新假設 |
| 4. 實作 | 建立測試,修復,驗證 | 錯誤已解決,測試通過 |
當流程揭示"沒有根本原因"
如果系統性調查揭示問題確實是環境相關、時序相關或外部的:
- •你已經完成了流程
- •記錄你調查了什麼
- •實作適當的處理(重試、超時、錯誤訊息)
- •為未來調查添加監控/日誌
但是: 95% 的"沒有根本原因"案例是調查不完整。
支援技術
這些技術是系統性除錯的一部分,可在此目錄中找到:
- •
root-cause-tracing.md- 透過呼叫堆疊向後追蹤錯誤以找到原始觸發器 - •
defense-in-depth.md- 在找到根本原因後在多層添加驗證 - •
condition-based-waiting.md- 用條件輪詢替換任意超時
相關技能:
- •hi-skills:test-driven-development - 用於建立失敗測試案例(第四階段,步驟 1)
- •hi-skills:verification-before-completion - 在聲稱成功前驗證修復有效
真實世界影響
來自除錯會話:
- •系統性方法:15-30 分鐘修復
- •隨機修復方法:2-3 小時的胡亂嘗試
- •首次修復率:95% vs 40%
- •引入的新錯誤:接近零 vs 常見