上一篇寫到,過去很多能力是在 Coding、Debug、查資料、寫錯再修改的過程裡慢慢累積起來的。這幾天重新回想以前做開發的日子,我發現 Debug 這件事情其實占了很大一部分,而且現在回頭看,真正留下來的也不只是怎麼下斷點、怎麼看 Log,而是碰到問題之後,會習慣先把事情弄清楚。
以前使用者來反映系統有問題,很少會把問題講得很完整。比較常聽到的是「剛才還可以,現在不行了」、「這一筆怪怪的」、「有時候會發生」。對工程師來說,光是這些資訊很難開始處理,所以通常還是得回頭把情境重新問一遍。是誰操作、什麼時間發生、用了哪一筆資料、前面做過什麼動作,換一個帳號或換一筆資料是不是還會發生,測試環境能不能重現。
很多問題真正開始有辦法查,通常都是從可以重現開始。
程式開發久了之後,也會慢慢知道畫面上看到的地方不一定就是問題發生的地方。畫面跳錯誤,不代表問題就在前端;API Timeout,也不一定只是 API 寫得慢;資料重複了,也不代表一定有人重複按了兩次。實際查下去,可能跟資料、權限、環境、版本、Network 有關,也可能是背景程式剛好正在執行。
那時候查問題的方式其實很單純,就是一個地方一個地方確認。先看 Log、查資料、追 SQL,再看程式到底走到哪裡。正式環境才會發生的問題,就開始比對測試機和正式機的設定、程式版本、Server 狀態。做久了之後,也會慢慢養成一種排除問題的習慣,不會一看到錯誤,就立刻往第一個想到的地方改程式。
我印象中以前有不少 Bug,最後查到的原因都跟最開始猜的不一樣。表面看到的現象是一件事,真正造成它的原因可能在前面很遠的地方。
假設是一套飯店訂房系統,偶爾會出現兩筆一樣的訂單。看到這個結果,第一個想到的可能是程式重複寫入,但真的一路往前查,也有可能是使用者送出訂單之後網路比較慢,前端沒有收到回應,又重新送了一次;也可能外部訂房平台在 Timeout 之後自己做了 Retry,而後端沒有辦法識別第二次進來的是同一筆 Request。
最後在資料庫裡看到的結果都是兩筆訂單,可是造成這兩筆資料的原因完全不同,後面要處理的方式也不一樣。如果只是把其中一筆資料刪掉,當下看起來解決了,過幾天可能還是會再發生。真的往下處理,就會開始碰到 Request ID、Retry、Idempotency,甚至要重新檢查整個交易流程。這些東西有些當年也不知道名稱,只是問題真的發生了,就得想辦法讓它不要再出現。
很多時候一個正常功能寫完,可能過一陣子就忘了,可是一些曾經查很久的問題,隔了很多年反而還記得。因為 Bug 常常會讓你看到原本沒有想到的情況。原本以為資料會照順序進來,後來才發現不一定;原本以為 Request 送出去、Server 收到就算完成,後來才知道中間還有很多狀況;原本以為使用者會按照設計好的流程操作,系統上線之後也常常不是這樣。
這些問題碰久之後,看系統的方式會慢慢改變。寫一個正常流程的時候,也會順便多想一些異常狀況。資料寫了一半失敗怎麼辦,外部系統沒有回應怎麼辦,同一個 Request 進來兩次怎麼辦。很多現在看起來很自然的思考,其實都是以前一次一次出問題之後留下來的。
工作內容慢慢從寫程式往系統規劃走之後,我發現做的事情其實也沒有差那麼多,只是問題變大了。以前可能是一支程式有問題,後來變成使用者說流程很慢、資料不準、兩套系統常常對不起來。這些事情沒有辦法直接改一行 Code 解決,還是要先回去看實際怎麼作業、資料從哪裡來、中間經過哪些系統,最後才知道真正需要處理的是哪一段。
以前看到一個 Error Message,可能先去搜尋、翻文件、找論壇,上網找答案時,常常找到大神的文章,幫我解決了很多問題,有些大神現在也成了好朋友,這是另外一種獲得,我很開心能夠認識這麼優秀的一群人,也很感激 :D
現在有了 AI,查問題方便很多。現在把訊息和相關程式丟給 AI,很快就可以得到一些可能的原因,連檢查方向都會一起整理出來。以前可能查兩個小時的東西,現在十分鐘就可以先排掉一大半。只是 AI 列出來的可能原因,不一定每一個都跟眼前的系統有關。系統的資料怎麼流、哪些程式是舊的、環境有哪些限制、使用者實際怎麼操作,這些背景如果沒有一起帶進去,最後還是可能改了很多地方,卻沒有碰到真正出問題的位置。
所以現在我使用 AI 查問題,跟以前最大的差別,大概只是手上的工具變多了。以前很多資訊要自己一個一個找,現在可以先讓 AI 幫忙整理,但看到一個現象之後,把情境還原、把範圍慢慢縮小,再確認真正的原因,這件事情好像還是沒有什麼捷徑。
回頭看以前那些 Debug 到很晚的日子,我現在當然不會想再重新體驗一次。今天如果 AI 可以讓我少查兩個小時,我一定會用。
只是那些年留下來的一個習慣,到現在我還是覺得很有用。
碰到問題的時候,不要太快開始找答案。
先弄清楚,我們現在看到的到底是問題本身,還是只是問題發生之後留下來的結果。