Debug 卡關,常常不是技術問題,而是心態與認知問題:我們會看見「想看見的」、會為了「證明自己沒錯」而忽略線索、會被「打開 DevTools 後 bug 就不見了」搞瘋。認識這些陷阱,比多學一個工具更能提升 debug 效率。
Debug 是為了蒐集資訊,不是自證清白#
這個心態轉變很關鍵。當你抱著「我要證明我的程式碼沒問題」的心態,你會下意識地避開那些可能打臉自己的測試路徑;當你抱著「我想知道這裡實際發生什麼」的心態,每一個錯誤都變成有用的線索,而不是對你的指控。
一個推論:沒有任何測試能證明「程式不存在 bug」,只能證明「在這些情境下沒發現 bug」。所以 debug 的合理目標不是「證明完美」,而是「把不確定性降到可接受的程度」。
把 bug 當成「資訊」而非「羞辱」。報 bug 的人是在送你情報;收到 bug 報告時防衛性地說「我這邊不會啊」「應該是環境問題」,只會擋住資訊流入。
觀察者效應:一觀察就變樣#
物理有觀察者效應,前端 debug 也有。打開 DevTools、加上 log、下中斷點這些動作本身,可能改變程式的行為:
- 開 DevTools 會停用快取(若勾了 Disable cache),於是「快取造成的 bug」就消失了。
console.log一個物件,可能讓某個惰性求值或非同步時序剛好錯開,bug 不再出現。- 中斷點暫停執行,改變了 race condition 的時序,競態 bug 就「好了」。
這類「一觀察就不見」的 bug 稱為 Heisenbug(對付它的策略見 間歇性 bug)。
因為觀察會改變現象,所以記錄 bug 發生時的「環境元資訊」往往比現場重現更可靠:瀏覽器與版本、是否開著 DevTools、網路狀況、快取狀態、裝置。這些脈絡常常才是真正的觸發條件。
認知偏誤:你只看見想看見的#
人在 debug 時有幾種頑固的偏誤:
| 偏誤 | 表現 | 對策 |
|---|---|---|
| 確認偏誤 | 只找支持「我的假設」的證據,忽略反證 | 主動嘗試「推翻」自己的假設 |
| 定錨 | 第一眼認定某處有問題,之後死守不放 | 寫下假設,被否證就果斷換 |
| 視而不見 | 看一段熟悉的程式碼太多次,反而看不到那個明顯的錯字 | 換人看、隔一段時間再看、大聲讀出來 |
小黃鴨除錯法#
把問題一行一行、原原本本地「講」給別人聽——哪怕對象只是桌上的橡皮鴨。很多時候話講到一半,自己就發現矛盾了:因為「向別人解釋」會強迫你檢視每個你原本「想當然爾」跳過的假設。
找不到人或鴨,就把 bug 寫成一段文字描述(要貼到 issue 那種)。寫的過程跟講的過程效果一樣——逼你把模糊的直覺說清楚。
何時該停下來#
前端的 bug 不是每一個都值得追到底。要在「繼續挖」和「先繞過/蒐集更多資料」之間做判斷:
- 這個 bug 多常發生?影響多少使用者?
- 已經投入多少時間?再投入的邊際報酬還高嗎?
- 是否該先加上更多 logging/監控,等它「自己再現」時帶回更多資訊,而不是現在硬逼重現?
對於罕見又難重現的 bug,「先加強可觀測性、把它養大到看得清楚」常常比當下死磕更划算。這不是放棄,而是換一種更有效率的蒐證方式。
小結#
- Debug 是蒐集資訊,不是證明自己沒錯;bug 是情報不是羞辱。
- 觀察會改變現象(Heisenbug);記錄環境元資訊比硬重現更可靠。
- 防範確認偏誤與定錨:主動推翻自己的假設;用小黃鴨除錯法。
- 罕見難重現的 bug,先加強可觀測性再蒐證,未必要當下硬攻。