It is a painful thing > To look at your own trouble and know > That you yourself and no one else has made it
— Sophocles, Ajax
核心概念#
“Bug” 這個詞用來描述「恐怖之物」可以追溯到十四世紀。海軍少將 Grace Hopper 博士(COBOL 的發明者)被認為是發現第一個電腦 bug 的人——字面上是一隻卡在早期電腦系統繼電器中的飛蛾。
沒有人能寫出完美的軟體,所以除錯會佔據你一天中很大的部分。讓我們來看看除錯中涉及的問題和一些尋找隱蔽 bug 的通用策略。
除錯的心理學#
除錯對許多開發者來說是一個敏感且情緒化的主題。你可能會遇到否認、指責、蹩腳的藉口,或純粹的冷漠。
接受除錯就是解決問題,然後以此態度來攻克它。
發現別人的 bug 時,你可以花時間和精力去責怪製造它的人。但在技術領域,你要專注於修復問題,而非責怪。
Tip 29 - Fix the Problem, Not the Blame(修復問題,而非責怪)
不管 bug 是你的錯還是別人的,它仍然是你的問題。
除錯的心態#
在開始除錯之前,重要的是採取正確的心態。關閉你每天用來保護自我的防禦機制,排除所有專案壓力,讓自己感到舒適。最重要的是,記住除錯的第一法則:
Tip 30 - Don’t Panic(不要恐慌)
如果你對 bug 的第一反應是「這不可能」,那你顯然是錯的。不要浪費一個神經元在「這不可能發生」的思維軌道上,因為很明顯它可以發生,而且已經發生了。
除錯時要小心近視。不要急於只修復你看到的症狀——更可能的情況是實際錯誤在你觀察到的現象好幾步之外。始終嘗試發現問題的根本原因,而不僅僅是它的特定表現。
從哪裡開始#
- 在開始看 bug 之前,確保你的程式碼能乾淨地編譯——沒有警告
- 收集所有相關資料,Bug 回報不是精確的科學
- 你可能需要親自觀看回報 bug 的使用者操作
除錯策略#
重現 Bug#
修復 bug 的最好方式是讓它可重現。我們不只要能重現,更要能用單一命令重現。如果你必須經過 15 個步驟才能到達 bug 出現的地方,修復它會困難得多。
Tip 31 - Failing Test Before Fixing Code(修復程式碼之前先寫失敗的測試)
有時候強迫自己隔離顯現 bug 的情境,你甚至會獲得如何修復它的洞見。
在陌生的程式碼中#
Tip 32 - Read the Damn Error Message(讀那該死的錯誤訊息)
首先,看看問題。是當機嗎?很多開發者看到紅色的例外訊息就立刻跳到程式碼中——請先閱讀錯誤訊息。
不良結果#
進入除錯器,用你的失敗測試來觸發問題。確認你也在除錯器中看到不正確的值。在 call stack 上下移動並檢查區域堆疊環境。
在手邊放紙和筆做筆記。追蹤線索時經常發現它不相關——如果沒有記下出發點,可能會浪費很多時間回溯。
二分搜尋法(Binary Chop)#
當面對大量堆疊追蹤時,可以用二分法在堆疊中間選一個框架,看錯誤是否在那裡出現。如果是,聚焦之前的框架;否則聚焦之後的。即使有 64 個框架,最多 6 次嘗試就能得到答案。
同樣的方法也適用於:
- 輸入值敏感性:二分資料集,隔離導致崩潰的特定輸入值
- 跨版本回歸:建立導致當前版本失敗的測試,然後在已知正常的版本和當前版本之間二分搜尋
日誌和追蹤#
除錯器通常聚焦於程式當前的狀態。有時你需要觀察程式或資料結構隨時間的變化。追蹤語句(tracing statements)對於併行處理、即時系統和事件驅動應用特別有用。
橡皮鴨除錯法#
一個非常簡單但特別有用的技巧是向別人解釋問題。另一個人只需要看著你的螢幕、不斷點頭(像浴缸中上下擺動的橡皮鴨)。他們不需要說一個字——逐步解釋程式碼應該做什麼的簡單行為,往往就會讓問題跳出螢幕自己宣布。
排除法#
Tip 33 - “select” Isn’t Broken(「select」沒有壞掉)
如果你看到蹄印,想馬——不是斑馬。OS 可能沒有壞掉,select 也可能完全正常。Bug 更可能存在於正在開發的應用程式碼中。
如果你「只改了一個東西」然後系統就停止工作了,那個東西很可能就是原因,不管看起來多不相關。
意外的元素#
當你被 bug 嚇到(也許甚至喃喃自語「這不可能」),你必須重新評估你所珍視的真理。你必須接受你的一個或多個假設是錯誤的。
Tip 34 - Don’t Assume It – Prove It(不要假設——證明它)
遇到意外的 bug 時,除了修復它之外:
- 判斷為什麼這個錯誤沒有更早被捕獲
- 考慮是否需要修改測試
- 檢查程式碼中是否有其他地方可能存在相同的 bug
- 如果花了很長時間才修好,問自己為什麼,以及如何讓下次更容易
- 如果 bug 源於某人的錯誤假設,與整個團隊討論
除錯檢查清單#
- 回報的問題是底層 bug 的直接結果,還是僅僅是症狀?
- Bug 真的在你使用的框架中?在 OS 中?還是在你的程式碼中?
- 如果你向同事詳細解釋這個問題,你會怎麼說?
- 如果可疑程式碼通過了單元測試,測試是否足夠完整?
- 造成這個 bug 的條件是否存在於系統的其他地方?
相關章節#
- Topic 24,死掉的程式不會說謊