對于程序員而言,Debug 是必不可少的能力,也是編程中的一大樂趣。大多數(shù)時候,我覺得 Debug 比寫程序本身要好玩得多。但是我發(fā)現(xiàn),很多人遇到了 Bug 就變得特別煩躁,完全不想理睬。很大的一個原因在于他們可能會花很長時間去 Debug,而且還有很大的可能是解不出來,只能求助于更有經(jīng)驗的人。
在這篇文章中,我會分享我最近的一次 Debug 經(jīng)歷,分析我在 debug 過程中用到的解決方法,希望可以給讀者一點幫助。
問題現(xiàn)象
在 IOS 客戶端上,有一個顯示訂單列表及一個顯示訂單數(shù)量的文本框。訂單可以根據(jù)狀態(tài)進行條件篩選,每次切換不同的篩選條件,會重新向后端發(fā)請求獲取列表數(shù)據(jù)。
假設(shè) A 狀態(tài)下有十條數(shù)據(jù),B 狀態(tài)下有 0 條數(shù)據(jù),問題表現(xiàn)是:從 A 狀態(tài)切換到 B 狀態(tài)之后,訂單列表正常顯示為空,而本應(yīng)變?yōu)?0 的訂單數(shù)量卻沒有變化,顯示的還是 A 狀態(tài)下的訂單數(shù)量 10。
出錯分析
一拿到這個問題,我的第一念頭是,bug 可能出在前端,也可能出在后臺,作為消費者,前端可能錯誤地顯示了訂單數(shù)量這個字段的值;作為提供者,后臺可能提供了錯誤的訂單數(shù)量。當(dāng)然極端情況下,問題由雙方共同造成??偠灾?,范圍比較廣,比較棘手。
診斷過程
下面是具體的解決過程:
跟著直覺走:從前端到后臺依次遍歷可能出問題的代碼
打開前端代碼庫,找到顯示訂單數(shù)量的文本框,開始查看它的數(shù)據(jù)來源以及顯示的代碼,不斷往上查找。
可是我發(fā)現(xiàn),由于我對相關(guān)代碼的上下文了解太少了,查找起來比較困難;另一方面,也由于我這兩天剛好生病了,精神狀態(tài)不好,很快我就失去了耐性,不想再看。
同時我也覺得這樣找下去不是辦法,相關(guān)的代碼很多,如果運氣不好,問題不在前端,我還得到后端查找原因,點背的話,可能一天下來都還找不到原因,更別說解決問題。
剎車,覺得不對勁的時候要停下來
從紛繁的代碼瀏覽停下來,起來走了走,又喝點水,待思維從剛剛的代碼中冷卻下來后,我開始從系統(tǒng)的角度考慮如何查找問題。
系統(tǒng)性思考
系統(tǒng)由兩部分組成,我大可使用二分法,先排除掉是前端還是后端的問題。系統(tǒng)由兩部分組成,而與錯誤有關(guān)的代碼非常多,如果一點點看過去,效率會非常慢。
不容易的方法
首先,我的目的在于找到出錯的地方,然后改掉錯誤;
所以,關(guān)鍵點在于找到出錯的地方;
從代碼相關(guān)的角度出發(fā),所有與 bug 有關(guān)的代碼都是懷疑目標(biāo);
在這個角度上,我必須看過所有的代碼才能確定哪里出了問題;
顯然,用這個方法,需要耗費比較多的時間精力。我隱約感覺,這個方法,從一開始就錯了,因為它太麻煩了,非常不自然。
新的切入點
所以,我開始思考,從什么其他的角度切入,可以更好地定位問題。這個時候,我想到了《如何解題》中說的 當(dāng)沒有思路的時候,回到問題本身。我再次打開 issue,查看上面是否有遺漏的條件。
- 看了看圖片,沒有什么收獲,跟之前一樣,只是顯示了錯誤的計數(shù)情況
- 看了看 issue 的描述,也沒有什么收獲,說的是這個 issue 在什么情況下會出現(xiàn)
- 看了看 issue 的提交時間,是三天之前
三天之前 ,欸,那三天之前的計數(shù)情況是正確的嗎?跟測試人員溝通了一下,他告訴我,這個 bug 是三天之前第一次出現(xiàn)的。
OK,感覺找到突破點了。
縮小范圍
既然 bug 是在三天之前引入的,那么就極有可能是三天之前的某一次提交導(dǎo)致的。如果我能找到這次提交,將它與正常的代碼做個對比,就可以極大地縮小錯誤的發(fā)生范圍了。
前端 OR 后端
接下來的第一件事,我需要確定,是前端還是后端導(dǎo)致的錯誤。
為了方便排查,我將三天之前第一個提交的前端代碼 (X) 簽出運行,保持后端為當(dāng)前最新的代碼,此時 Bug 現(xiàn)象消失,哈,那就可以確定錯誤是發(fā)生在前端了。
持續(xù)縮小范圍,精確到提交歷史的哪些提交出現(xiàn)問題
將前端代碼切換到當(dāng)前代碼庫的最后一個提交,Bug 出現(xiàn)。很好,確定 bug 就是在三天之前到兩天前這個時間范圍內(nèi)引入的。接下來,只要從 X 開始,一個提交一個提交地往前排除,找到第一次出現(xiàn)問題時的提交,查看下它與前一個提交的差異,就可以確定問題根源了。
問題在于,在這段時間之內(nèi),代碼庫的提交數(shù)量有 20 個,一個一個地往過看,運氣不好的話,得找 20 次,費勁,不予采用。
無敵的二分法
看著提交歷史,靈光一現(xiàn),想到了 二分法,采用這個方法的話,在 20 個提交中,最多只需要 5 次就可以找到目標(biāo)了。
哈,腦子興奮起來之后,又一下子想到了 git 的 bisect 命令,這就是用來在提交歷史中作二分查找的呀。
果不其然,沒有幾下子,第一次出現(xiàn)問題的提交就被我用 git bisect 揪了出來。
修正錯誤
使用 Intellij IDEA 的文件歷史對比功能,發(fā)現(xiàn)在這次提交中,修改的主要是分頁器的代碼。
于是,我找到提交這次代碼的同事,給他把現(xiàn)象一描述,他一下子就想到了大概出問題的地方,三兩下,改好了代碼。
總結(jié)
在 debug 的過程中,有時候找不到思路,可能饒很久都找不到問題根源,就像走進了岔路就走不出來一樣,時間久了,可能都忘了自己在干嘛了。遇到這種情況,千萬不要死磕硬撐,此路不通,就換條路,覺得難走,也換條路。我在這次debug 的過程中,也遇到了這樣的情況,一開始就從紛雜的前端代碼入手,看了很多都看不出個所以然,自然而然就變得煩躁了。重要的是,遇到障礙后,及時讓思維慢下來,不要沉浸在 debug 的狀態(tài)里出不來。冷靜下來之后,試一試從
新的角度去觀察問題,并且持續(xù)問自己,有沒有更簡單的方法。
至于我在這次 debug 中用到的技術(shù)和方法,全都是優(yōu)秀的書籍上學(xué)來,類似的書籍有很多,建議多看。
拓展閱讀
《突破思維的障礙》
《如何解題》
《編程珠璣》
《軟件調(diào)試實戰(zhàn)》