實習的時候,我覺得驗 bug 是整份工作裡最輕鬆的一塊。
RD 說「我改好了,你測測看」。我照著單子上的重現步驟走一遍,沒有重現,我就把單子關掉。
十分鐘的事。
證明一個 bug 存在,很簡單。你只要找到一個反例——一張跑版的截圖、一份 crash log、一次算錯的金額。一個就夠,事實成立。
證明一個 bug 不存在,是另一回事。
你測了五次沒重現,不代表第五十一次在某個網路延遲、某個高併發、某個記憶體狀態下不會再出現。你是在用有限的次數,去斷言一個近乎無限的狀態空間裡沒有那個東西。
Day 8 講測試是抽樣。驗 bug 是抽樣的極端版本——你只取了一個樣本,然後宣告整個母體乾淨。
我實習的時候當然不知道這些。我只知道我點了一次,沒壞,所以修好了。
後來我把這件事拆成兩種難,處理方式完全不同。
認識論難:系統狀態空間本來就窮盡不了,理論上你拿不到 100% 的信心。偶發的 race condition、時序相關的鎖死,你測不出來不代表它不在。
成本難:邏輯上這個 bug 是可驗證的,只是驗證的範圍遠大於修復的範圍。
第二種最常被低估,而且低估的人不只是 RD,還包括當年的我自己。
| Bug 類型 | 修復 | 驗證 |
|---|---|---|
| 按鈕沒跳轉 | 改一行 | 走一次那條路徑就好 |
| 改一個設定檔參數 | 五秒 | 要跑過所有受這個設定影響的服務 |
| 兩個模組接起來壞掉 | 改一側 | 兩側之間的互動邊界全部要重驗 |
| 高併發下崩潰 | 改一行 | 要在測試環境複製出規模與時間 |
中間那兩列是最容易吵架的地方。RD 花五秒改完,覺得這是個小 bug;QA 說要兩天,聽起來像在刁難。
雙方都沒有錯。他們講的是不同的範圍。
「我改了那行,應該修好了,你測測看。」
這句話聽起來像是交棒,但它同時做了一件沒有被說出口的事:它把「這個系統還會不會出事」的最終不確定性,移到了你身上。
我實習的時候接下了那個責任,而且完全沒有意識到自己接了。我只覺得我在做一件例行公事。
這也是第一週那個模式的又一次:有人在擔心某件事,而那個擔心沒有被說出口。RD 擔心的是他改的東西還有沒有漏。他把那個擔心用一句「你測測看」交出去,然後那件事就變成我的了。
既然證明不存在在哲學上做不到,那總不能永遠不關。
《Lessons Learned in Software Testing》第 185 條給了我目前看過最好用的標準:「足夠的測試」的意思是,足夠讓你的客戶做出明智決策的資訊。
測試的本質是蒐集資訊,不是追求完美。停不停得下來,不看你有沒有達到哲學上的乾淨,看你有沒有給決策的人夠用的判斷材料。
台灣測試社群前輩 David Ko 在〈什麼時候測試可以停止?〉整理過幾個實務指標:高優先級的 bug 都解掉了、bug 發現率開始收斂、預算或時程到了、專案風險降到可接受的範圍。其中「發現率收斂」那條我覺得最少人注意——如果你還在一直找到新東西,就算此刻手上沒有未解的 bug,也代表底下還有沒被翻出來的。
我當年關單只寫兩個字:已驗證。
現在我會寫三行:
我在什麼條件下驗的(版本、環境、資料、重複幾次)
我沒有覆蓋到什麼(哪些情境沒驗、為什麼)
我對這次修復的信心程度,以及理由
這跟 Day 2 的「我測了什麼、沒測什麼、為什麼沒測」是同一件事,只是縮小到一張單子上。
寫這三行不會讓 bug 更不可能復發。它改變的是別的事:如果它真的復發,團隊手上有一份紀錄,知道當初驗到哪裡、沒驗到哪裡。 復發不再是一次糊掉的意外,而是一個可以往下追的線索。
而且它把那次轉嫁講明了。我沒有拒絕接下那個不確定性——實習生也拒絕不了——但我把它的邊界畫出來,放回所有人都看得到的地方。
關單之前,問自己一句:
如果這個 bug 下週復發,我現在寫的東西,幫得上那時候的我嗎?
明天聊:回歸還是冒煙,以及當別人要求的東西聽起來不合理的時候,怎麼先把他的話講完。