「程序員
1、概要web
歡迎收藏。當你遇到很是難纏的Bug時,不妨回來這裏看看…編程
編程原本是一項很是優雅的工做,而程序員,也理應是那種如藝術家通常,不斷寫出極具觀賞性和創造力的做品的職業。 然而自從工做以後,卻發現身邊的程序員大多深陷趕迭代出口和修復緊急Bug的泥潭。常常能夠看到下面這些場景:服務器
修復了一個Bug,卻引入了更加致命的Bug
不停的修改代碼,重啓服務器,Bug依然沒有修復
補丁打上去後,Bug依舊存在,定位了好久,最後發現是補丁打錯地方了
雖然咱們應該儘量保證提交上去的代碼裏Bug越少越好,可是當Bug真的出現時,咱們每每十分慌亂,像一隻無頭蒼蠅同樣,調試的過程毫無章法。併發
直到最近偶然看到了這本書——《調試九法》,這是我看到的第一本講調試方法論的書,回想本身以前在解決Bug時的常常遇到的手足無措,像無頭蒼蠅通常四處亂撞,最後瞎貓碰上死耗子般地解決了Bug,我絕不猶豫地買下這本書。app
這是一套系統的調試理論。框架
但願本文介紹的這套調試學的知識,可以幫助你在從此遇到Bug時,思路更加清晰,心情更加淡定,從容不迫地去解決問題。webapp
「編程語言
2、調試法則總覽微服務
這套方法論能夠總結成一句話:
調試是一門科學,任何不懂原理就進行的操做都是耍流氓。
這句話能夠分爲兩個步驟進行實踐:
準備工做:
若是你要調試這個系統,首先你必須先理解它。
在開始調試以前,檢查一下「插頭」,別由於一些簡單的問題而瞎忙活半天。
肯定「插頭」沒有問題以後,你須要重現Bug。
尋找緣由並解決Bug:
不要認爲你猜想的緣由是對的,要去觀察,驗證你的猜想。
採用分而治之的方法,定位多模塊的系統問題和修復由多個子問題引起的問題。
一次只修改一個地方
作好修改記錄
若是正向調試走不通,不妨試一下反向調試法
最後一招:求助他人
下面對這些法則逐一進行詳細地介紹。
「
3、理解系統
理解系統是定位Bug的前提條件。
我以前遇到程序拋出異常時,常常就把異常信息貼到網上搜,而後把網上的解決方案執行一遍,有時work了,就大吉大利,可大多數時候,是不work的,緣由很簡單,也許對方的JDK版本跟你的不同,也許大家倆只是報錯信息相同,可是拋異常的緣由不一樣,更大的可能,對方的解決方案原本就行不通。
這就能理解爲何理解系統是定位Bug的前提了,若是你在定位一個JDK異常,那麼至少你要掌握Java SE吧,若是你能掌握JVM的垃圾回收原理、類加載機制,天然更好;而若是你在定位一個支付系統爲何沒有把帳打到客戶的帳戶,那麼你得了解支付的流程吧。
總之,在開始調試以前,咱們得弄清楚,調試是一門科學,而不是一門機率學,你須要理解整個系統,纔可以進行調試。有如下方法能夠幫助你理解這個系統:
閱讀手冊:閱讀需求設計文檔、產品文檔、使用手冊等
仔細閱讀手冊的每個細節:說不定解決Bug的方案就在某個段落裏
掌握基礎知識:最後你老是要看源碼的,至少你要掌握相應的編程語言把
瞭解工做流程:從總體的角度來觀察,而不是作井底之蛙
能夠說,理解系統的最終目的就是爲了瞭解工做流程,瞭解了工做流程,你纔可以從總體的角度來觀察,否則就像井底之蛙,覺得問題必定出在本身這個模塊,而其實問題是在上游的某個模塊裏。這一點,和《程序員的思惟修煉》中提到的,「專家從總體進行思考」的觀點不謀而合。
「
4、檢查插頭
這裏固然不是讓你去問人家插頭插了沒,插頭在這裏是泛指一切讓產品正常運行的基本要求。這些基本要求一般咱們都認爲理所固然是正常的,可事實有時並不是如此。
好比你的一個系統,須要在配置界面配置白名單,否則上游的請求就會被拒絕,那麼當出現問題時,你應該首先去檢查一下這個白名單配置了沒,由於對方有多是個新手。
甚至當出現一些不可理喻的錯誤時,你要去軟件的運行目錄下,好比Tomcat的webapps目錄,看看軟件包是否是完整。
當你替換新代碼上去後,發現Bug依然存在時,不妨上去看看正在運行着的,是否是仍是舊代碼。
書中把這條規則放到了倒數第三條,我這裏把它放到第二條,緣由很簡單,一般咱們在發現Bug或者別人跟本身說這裏有Bug時,內心都會慌,都會緊張,因此不妨先檢查一下插頭,緩解一下本身緊張的心情,同時也強迫你從總體的視角進行觀察,不會侷限在一個小模塊裏。
「
5、重現失敗
這幾乎是一個下意識的動做,就算你以前沒讀過這本書,在遇到Bug時,你也會去嘗試重現它,緣由很簡單:
重現失敗讓你能夠觀察失敗發生時的上下文信息,進而找到失敗的緣由
重現失敗讓你能夠判斷是否已經修復了問題。
有些問題很好重現,而有些呢,倒是要在特定的輸入的狀況下才會出現的。
咱們犯的絕大多數錯誤是在重現的方式上,做者對重現提出了兩條原則:
模擬失敗發生的條件,可是不要模擬失敗的機理,由於你認爲的致使失敗的機理極可能是錯誤的。舉個例子,你認爲是高併發致使的bug,因而你模擬了高併發的環境,問題重現了,而後你就說是高併發致使的,其實呢,只不過是高併發提升了問題發生的概率。
隻影響錯誤發生的頻率,不影響錯誤發生的方式。其實高併發的環境能夠用來提升錯誤發生的頻率,只不過你要在問題重現時,要找到相應的日誌信息,而後定位出問題發生的緣由,而不是直接認爲就是併發致使的。
「
6、不要猜,要觀察
如今咱們能夠重現Bug了,直覺告訴我要在那個地方進行一個字符串編碼的轉換,且慢,在進行這個武斷的嘗試以前,先來看看《福爾摩斯》是怎麼說的:
主觀臆斷的人,老是爲了套用理論而扭曲事實,而不是用理論來解釋事實。
猜想只是爲了肯定搜索的重點,可是在開始修復以前要觀察確認你的猜想。因此在咱們修改代碼以前,仍是看一下發生錯誤時的日誌信息,還能夠調試一下代碼,在必要的時候打開源碼深刻研究一下,肯定確實是字符串編碼的問題,再去修改代碼。
有人說,那我直接改代碼,而後看結果不就知道是否是字符串編碼的問題了。固然不是,要知道一個問題的產生多是由多處地方的代碼引發的,也許解決完這個字符串編碼的問題,還須要解決另外一個問題,才能把整個問題解決呢?若是你在修改前就沒進行觀察,就會認爲此次修改毫無心義,這樣整個調試過程就會陷入死局。
記住,調試是一門科學,任何不懂原理就進行的操做都是耍流氓。
「
7、分而治之
系統一般都是由不少個模塊組成的,這也就要求咱們要檢查不少個模塊的日誌纔可以肯定問題發生的緣由。尤爲是如今流行的微服務框架,一筆業務出現問題,你須要到不少個服務的機器上去找日誌。
可是,若是你的業務執行是線性的,也就是說若是節點A執行失敗,那麼節點A以後的也都會執行失敗,那麼你就能夠採用二分法的方式來定位了。要知道,在1到100裏猜一個數字,最多也就須要7次。
採用二分法的方式,你將逐步縮小嫌疑的範圍,最終找到問題的根因。
固然,若是問題是由多個子問題引發的,那麼記住,找到一個,消滅一個,這就是所謂的分而治之。
「
8、一次只修改一個地方
經過觀察,你認爲你的修改方法會起做用,但若是實際上你修改完代碼以後,並無起到任何做用,那麼請你立刻改回去,以避免這個修改引入了新的Bug。
「
9、作好修改記錄
把你調試過程當中的操做和結果按順序所有記錄下來,方便你在發現作了那麼多處修改依然沒有解決問題時,進行回溯,反思本身的操做有沒有不對的地方。
「
10、反向調試法
上面的調試規則,都是從問題出發,去尋找犯錯的代碼。但有時候反過來也許會更好。
你能夠找到最新一個能夠正常運行的版本,而後對比如今這個版本和那個版本之間的差異,經過分析改動的代碼,來分析是哪塊代碼致使的問題。
「
11、求助他人
有時候問題比較緊急,這時候不妨問一下專家,正如《程序員的思惟修煉》中提到的,專家依靠直覺,他們每每會一針見血的給你指出問題的地方。
若是你對系統有必定理解的狀況下,能夠上軟件供應商的官網、谷歌、StackOverflow等網站尋找相關的資料。
在求助的過程當中,你只須要描述問題的症狀,若是對方沒有要求,那麼不要給他講本身的理論,以避免將對方帶入本身的思惟定式。
而在給他人描述問題的同時,你本身可能也會獲得啓發。
「
12、總結
以上就是我看完《調試九法》這本書以後總結的一套調試方法論,固然仍是建議你們看一下原著,說不定會有新的收穫。不過書中列舉了大量的例子,多的讓我感受有些冗餘,建議你們看的時候,先看每一章節的開頭,和每章結尾的小總結,看完以後有不理解的,再去看每章中間的案例。
本書的例子雖然大多數都是關於工程技術的,可是裏面的一些想法仍是能夠借鑑到生活中去。好比,夫妻吵架了,表面看上去是由於丈夫不肯意洗碗,可是若是你能從全局的角度去觀察,你就知道,實際上是由於丈夫情人節時沒有給夫人買禮物。
回過頭來看這些規則,其實咱們在工做和生活中時不時都會用到,可是咱們以前一直沒有一個系統的理論體系,在掌握了書中介紹的調試規則以後,咱們在從此定位錯誤根源時,會更加層次分明,從容不迫。
最後必須說一句,專業人士應該在開發時就儘量地保證軟件的質量,而不是常常靠調試來彌補缺陷。完善而充分的單元測試是保證代碼質量的關鍵。當你發現Bug時,也就說明你的測試用例不全,在修復完Bug以後,要及時補上測試用例。