如果現在要我從 PawPal 裡挑一個最有印象的 Bug,我反而很難只選出一個。
不是因為當時沒有遇到問題。
而是到了專案後期,Bug 真的一個接著一個。
有時候畫面直接跳出 Error。
有時候 API 看起來有回資料,但畫面就是不對。
有時候程式看起來好像也沒什麼問題,實際操作的結果卻跟想像中完全不一樣。
甚至還會遇到:
好不容易把這個修好了,怎麼另一個地方又壞了?
所以比起記住某一個 Bug,我更記得的是自己那時候慢慢養成的習慣。
遇到問題之後,不要一直亂改,而是先想辦法把問題的範圍縮小。
剛開始寫程式的時候,只要 Console 出現一大片紅字,我第一個反應通常不是:
好,我來看看它告訴我什麼。
而是:
完了,又壞了。
而且最麻煩的是,就算錯誤訊息已經出現在眼前,我也不一定看得懂真正的原因。
畫面壞掉了,問題可能在前端。
但也有可能是 API 回傳的資料不對。
再往後可能是後端。
甚至最後才發現跟資料本身有關。
對當時的我來說,常常會變成:
畫面不對
↓
是前端嗎?
↓
還是 API?
↓
後端?
↓
資料?
真正困難的不是「知道有 Bug」。
而是:
到底是哪裡有 Bug?
做 PawPal 做到後面,我才慢慢開始有一個比較固定的習慣。
遇到問題時,我會先看 Console。
看看有沒有明顯的 Error,或是哪個檔案、哪一段程式出問題。
接著會看 Network。
確認:
Request 有沒有真的送出去?
↓
API 有沒有回來?
↓
回來的資料是不是我預期的?
如果 Request 和 Response 看起來都正常,我才會再回頭看:
我剛剛到底改了什麼?
有時候真正的問題,反而就在剛剛那幾行修改裡。
所以後來我的排查方式,慢慢變得比較像:
Console
↓
Network
↓
回頭看剛修改的程式
↓
先判斷問題可能在哪個範圍
現在看起來好像很基本。
但對當時的我來說,最大的差別是:
我開始不是看到 Bug 就一直改,而是先找線索。
在修 Bug 的時候,我當然也會用 AI。
尤其是遇到看不懂的 Error,或是我不知道問題到底比較可能出在哪個檔案、哪一層時,AI 確實幫了我很多。
但我的方式通常不是一看到錯誤就直接全部丟給 AI。
比較接近:
自己先看
↓
自己先找
↓
先做自己的判斷
↓
先試著改一次
↓
再把目前的狀況交給 AI 確認
有時候我會請 AI 幫我解釋:
這個 Error 到底在說什麼?
有時候則是:
我現在已經查到這裡了,問題比較可能在哪裡?
對我來說,這樣最大的差別是:
我不是只拿一個答案回來貼上,而是先有自己的判斷,再用 AI 幫忙確認。
如果現在要我直接說:
PawPal 哪一個 Bug 當時最讓我印象深刻?
老實說,我很難說是哪一個。
所以寫這篇之前,我重新翻了一次當時留下來的 Git 紀錄。
結果看到一個自己確實修過,也很能代表那段 Debug 經驗的例子:
醫院評論 Modal 的過期 Request。
這不是一個我現在還能清楚回想每個操作步驟的 Bug。
而是我重新看 Git 紀錄後,才重新看到自己當時處理過這個問題。
這個問題跟醫院評論 Modal 有關。
假設使用者先打開醫院 A:
打開醫院 A
↓
送出取得評論資料的 Request
↓
等待資料
但資料還沒回來之前,使用者已經切到醫院 B。
這時候就可能變成:
醫院 A 的 Request 還在等待
↓
使用者已切到醫院 B
↓
A 的 Request 最後才回來
Request 本身其實沒有失敗。
資料也成功拿到了。
但問題是:
使用者現在看的已經不是醫院 A 了。
如果舊 Request 回來後還直接更新目前畫面,就可能把現在的狀態蓋掉。
這讓我重新看到一件很有趣的事:
Bug 不一定是 API 失敗。
有時候 API 明明成功了,只是它回來的時間已經不對了。
從 Git 留下來的修改可以看到,當時加入了 Request Token 的處理。
如果用「簡化概念」來看:
第一次 Request
→ Token 1
第二次 Request
→ Token 2
如果 Token 1 比較晚才回來,就先確認:
我現在還是最新的 Request 嗎?
如果不是,就不要再更新目前的畫面。
舊 Request 回來
↓
確認 Token
↓
已經不是最新
↓
忽略結果
現在回頭看,真正重要的不是記住「Request Token」這個名詞。
而是開始理解:
一份資料能不能拿來更新畫面,不只要看內容對不對,也要確認它是不是還屬於現在的狀態。
這個修改也有留下對應的測試。
確認在切換醫院、關閉 Modal,或相關評論操作之後,過期的 Request 不會再把舊狀態寫回來。
現在回頭看,這也讓我更確定一件事:
修 Bug 不是:
改完
=
結束
而比較像:
找到問題
↓
修改
↓
重新測原本情境
↓
確認沒有影響其他相關流程
這也是做 PawPal 時,我很常有的一個感覺。
為什麼剛剛那個明明修好了,現在又有另一個地方怪怪的?
以前遇到這種狀況會很煩。
因為會覺得:
我不是已經修好了嗎?
但做到後面,我才慢慢知道,一個功能通常不是真的只跟自己有關。
資料可能共用。
狀態可能共用。
同一個元件也可能在不同地方被使用。
所以修正一個地方之後,還是要確認其他相關流程有沒有被影響。
我對「修好」的理解也開始改變。
不是:
原本那個 Error 不見了。
而是:
原本的問題消失,而且相關功能仍然正常。
寫一個新功能時,我比較常想的是:
我要怎麼把這個東西做出來?
但修 Bug 時,我腦中一直出現的是:
為什麼?
為什麼資料會變成這樣?
為什麼 Request 明明成功,畫面卻不對?
為什麼這段程式之前可以,現在不行?
一直追這些「為什麼」的過程,反而逼著我重新去理解原本的程式到底是怎麼運作的。
有時候查很久,最後找到真正原因的那一刻,會有一種:
喔,原來是這樣。
的感覺。
那種感覺其實跟把一個新功能做出來不太一樣。
現在如果再遇到 Bug,我一樣不一定能馬上知道原因。
也還是可能要查很久。
但跟剛開始最大的差別是:
我現在至少比較知道第一步要做什麼。
不是看到 Error 就一直改。
而是:
先找線索
↓
確認現象
↓
縮小範圍
↓
再決定要改哪裡
我不一定可以立刻找到答案。
但至少開始知道,要怎麼一步一步靠近答案。
寫新功能確實會讓我很有成就感。
但回頭看 PawPal 這段開發過程,我反而覺得:
真正逼我去理解程式為什麼這樣運作的,很多時候都是那些一直出現的 Bug。
也許這就是為什麼,修 Bug 的過程,最後反而讓我學到比寫新功能更多。
修 Bug 的時候,我慢慢發現很多問題表面上出現在前端,真正往下追之後,卻不一定只跟前端有關。
尤其當資料開始互相關聯後,如果一開始沒有想清楚,後面要修改時真的會很痛苦。
下一篇:
Day 27|資料庫設計:一開始沒想清楚,後面很痛苦。