程序猿如何快速高效的改 bug?改bug都有哪些技巧?

本人編程水平不高,還在努力學習中,

有時候遇到未知 bug 頭都大了(??ˇ?ˇ??)

有時候一直改一直改,

結果越改越煩Σ(-`Д′-?;)?

不知道有什麼可以提高改 bug 效率的方法和工具,希望各位大大賜教啦,輕噴啊各位

去年問的問題突然多了這麼多回答,謝謝各位答主



復現-&>構建簡單化例子-&>解決,前兩步最重要。


在知乎上的格式比較爛,鏈接也沒有了。推薦去原文閱讀: 如何快速高效地修 bug? - 依雲amp;amp;#x27;s Blog

看回答,有一些可操作性很強的答案。但是呢,你知道的,考試好不代表能力強,如果你只是學習別人的方法而並不理解,那麼學來之後只會是東施效顰而不能融會貫通。所以呢,我也來發表一下自己的見解。

首先,你要定位 bug。這時,你需要:

  1. 注重邏輯性。不要做沒有證據的結論。如果你有猜測,就去證實或者否定它。比如某次,同事代碼返回的數據有問題,認為是緩存用的 Redis 有問題,返回了錯誤的數據。然而沒人去對此猜測進行求證……我去確認了一下,Redis 收到了請求,並且響應正常。接下來,排除所有其它可能的原因之後,最後剩下的那個就是真相。真相就是,代碼里有個 } 的位置放錯了,因為它剛好在一屏之後的位置,所以沒有人發現……(是 Vim 幫我找到的)
  2. 基本的方法論。比如二分法。比如最小化測試用例。如果你要提問,要懂得提問的智慧,不管是向搜索引擎還是向人,你都需要提出正確的問題。
  3. 知識面。你寫 Web 後端的話,普通的 HTTP 得懂,瀏覽器的開發者工具得會用。簡單的 JavaScript 也有會點兒。簡單地說就是,你要精於你自己主攻的部分,然後要熟悉你的上下游。再比如如果你使用 CPython 的話,你要準備一份 CPython 的源碼,並且要能夠流暢地閱讀 C 代碼。
  4. 工具。工欲善其事,必先利其器。一大堆調試用的工具,你至少得知道它們能幹什麼,需要的時候能用。比如 strace、lsof、gdb、git bisect,還有高級點的 sysdig、systemtap、perf 等等。當然還有一堆不是專門為調試而設計的通用工具,比如 the silver searcher 或者 ripgrep。一個快速的全文搜索工具能幫你在最短時間內找到相關的代碼或者日誌。你不必成為正則表達式大師,但是簡單的一定要會,不然面對上千個匹配結果你要怎麼辦呢?Vim 有一個插件 Mark,能夠同時高亮多個模式,非常利於調試期間閱讀代碼和日誌。投入時間學習使用高效的工具,不要把時間浪費在等待和人工搜索上,也不要讓自己忙於瑣事而斷了靈感和線索。

最後,不要不斷地、毫無目的地換個環境啦,換個版本啦,換個用戶啦,這樣子找問題。如果這樣做很有效的話,大家都去買彩票去了。

找到 bug 之後,理解它是如何產生的。當你理解之後才能真正修好它。就像你感冒了吃抗生素,根本沒有用。


對於大部分開發人員來說,你所經歷的絕大部分BUG已經被別人修復並且分享出來了,這時候百度、Google、Stackoverflow這些工具是你最好的幫手。

但是每個開發都可能會遇到一些疑難的BUG,可能好幾天都不得其解,搞得人焦頭爛額,這時候就不要左改一下,右改一下胡亂試驗了,而要冷靜下來,理理頭緒再動手。

先試一下下面的步驟:

1. 換個環境試試

2. 換個用戶試試

3. 換個操作方式試試

4. 換一下數據試試

5. 換個瀏覽器試試

6. 換個版本試試

7. 換個人操作試試(哈哈)

再搞清楚下面這幾個問題:

1. 這個BUG什麼情況下出現?什麼情況下不出現?兩種情況的區別在哪裡?

2. 這個BUG之前沒有,現在出現了,中間都動了什麼?

3. 這個BUG生產環境出現測試環境不出現,兩個環境區別是什麼?

4. 同樣的功能,這樣操作沒有BUG,那樣有BUG,兩個操作的區別是什麼?

這些問題搞清楚了,可以採用代碼回退、修改配置、環境替換等方式驗證BUG是否會消失,然後再找其中的原因。

下面列出一些常見的疑難BUG類型以及每種BUG的診斷方法:

1. 輸出結果與預期不符,這種BUG一般都是由於代碼邏輯錯誤造成的,如果能在開發環境重現,最好解決方法就是單步調試,設定每一步代碼的預期結果,然後跟蹤判斷實際結果是否與預期結果一致,不一致的分析原因,如果在開發環境無法重現,無法單步調試的,可以採用添加輸出日誌的方式判斷哪一步的問題。

2. 系統異常報錯,這種情況下需要提取日誌,找出錯誤堆棧信息,這時候最重要的事情是要把堆棧信息看懂、看完整。這是很多經驗不足的程序員常見的問題,就知道報錯了,報的什麼錯,這個錯代表什麼一概不知。而且往往堆棧信息是一個套一個輸出的,比如Java裡面表象上看是一個NullPointer Exception,但是如果你看到底,就會告訴你到底是什麼錯誤引發了這個NullPointer。

3. 系統Crash,這個問題常見的原因是負載過高、並發過高、或者配置錯誤。最常見的就是內存溢出。這時候要首先排除配置錯誤的原因,主要靠查看Crash Log來分析原因,如果Crash Log沒有有用的信息,就得排查硬體、內存、網路等方面的設置,看是否有配置錯誤的地方。再找不到就在測試環境用開發模式進行壓測和調試。

4. 系統響應緩慢,這種問題一般是存在資源競爭或者系統資源不足的情況,舉一個Java應用的例子來說,如果某些功能出現系統響應慢,處理步驟如下:

  • 查詢TCP鏈接數是否超出系統可承受範圍,命令:netstat -nat | awk "{print $6}" | sort | uniq -c | sort -n , 主要查看ESTABLISHED、TIME_WAIT、CLOSE_WAIT這幾個數量是否異常過大。

  • 查詢Java線程數是否正常,命令:ps -eLf | grep java -c

  • 查看和分析gc情況,命令:jstat -gcutil 76691 1000 5

  • 用jstack查看Java線程狀態,命令 jstack pid &>&> xxxxx.txt,然後從這個文件裡面分析java線程中各種狀態線程的數量,命令:grep java.lang.Thread.State xxxxx.txt | awk "{print $2$3$4$5}" | sort | uniq -c , 如果有非常多的blocked和deadlocked狀態的線程,這個地方就是問題所在了。

通過這些步驟,找出那些功能、那個方法、那段代碼存在瓶頸和資源競爭,針對性的對這個地方進行改造就行了。

最後,如果某個地方出現BUG實在找不出什麼原因,機謀用盡,那就上我們的絕招「小黃鴨調試方法(Rubber Duck Debugging」吧。

1. 去買一隻小黃鴨,就下面這樣的,注意個頭不要太大。

2. 把小黃鴨放到電腦屏幕上方,就下面這樣的,最好面對著你。

3. 打開你出問題的那段代碼,面對小黃鴨,用手指著代碼,一行一行的給它解釋一下這行代碼是幹什麼的,為什麼這麼寫。

4. 現在知道問題在哪了吧?


1.復現,找到穩定復現的辦法

2.抽特徵,對BUG發生的條件進行抽取

3.減特徵,替換掉一部分發生的外部條件,看BUG是否發生

4.流程分析,對BUG對應的流程進行徹底的分析,藉由之前的抽特徵減特徵,進行排除法

5.如果沒有明顯錯誤,二分定向,記錄相關日誌

調BUG最主要的就是你要有一個清晰明確的認識,我見過很多人調BUG都是這改改那改改,扔進去的數據也沒有針對性。調BUG最重要的還是對自己寫的代碼有一個清楚的認識,如果你不會調BUG,一定是因為你寫代碼時思路不夠清晰。。。


好好學慣用gmock和gtest,在開發的時候就每一步做好單元測試,保證你的每一塊積木都是正確的。

如果你的bug不是語法錯誤或者崩,而是邏輯錯誤,而你有沒有單元測試來narrow down,再牛的人也沒有系統的方法幫你找…


女裝 -&> 異常棧 -&>看日誌 -&> 斷點 -&> 單步


工作久了,每個人都有一套屬於自己的解決問題的方法,解bug也一樣,結合我自己的實際工作經驗,總結出解決bug的流程如下:

  • 復現問題:
  1. 提高效率的最好方式是在看清楚bug描述之後再去操作復現;
  2. 根據bug描述比較困難復現的情況下,應該重點分析程序日誌,通過對日誌的分析結果去復現問題;

  3. 對於隨機問題,切記不要隨意破壞現象,很難操作出來的,你搞不定可以找高手幫忙搞;
  • 定位問題

  1. 首先應該看日誌,找到報的異常信息,如果無法定位問題,應該藉助谷歌搜索,看看別人是否遇到過類似的問題;
  2. 如果無法通過日誌定位問題,需要藉助一些方法來定位問題,比如調試、列印、排除法(逐步屏蔽代碼,看問題是否仍然存在)、對比(適用於新增問題)、自言自語法(小黃鴨調試法)。
  • 針對性的解決方案
  • 改善

  1. 不要為了改bug而改bug,不然就算你改好了也是暫時的,會導致更繁複的工作量(比如導致其它問題、導致兼容性問題等);
  • 驗證

  1. 提測之前一定要自己驗證,很多時候導致修改bug效率低下的原因就是自己修改後沒有驗證,導致提測的程序不穩定,導致返工;

涉及到具體每個步驟又有針對性的方法和技巧,每個步驟的具體方法有時間再更。


由於之前經常接手二手、三手、四手...代碼,所以簡單總結了一下經驗:

一、復現

修改bug第一難點就在於不知道bug在哪裡,什麼情況下回出現,或者本身就不是一個bug而是用戶操作失誤。所以有效精準的復現過程和信息就相當重要。

感受一下不同的反饋

用戶反饋的信息:App不能發圖,什麼破軟體

程序員需要信息:

聯網沒?網路是2G、3G、4G還是wifi?

APP版本信息,是最新版嗎?

圖片大小,單反照的?

操作步驟常規不?

我們提供這個功能了嗎?

....

所以只要精準定位復現步驟,bug就基本解決了一半。

感受一下微信收到的『bug反饋』

抱歉,可能太多鍋需要背.... 微信為什麼不能發電?

二、找資料和幫助

分兩種情況

1、自己寫的代碼

用好日誌、Debug等各種工具,然後針對問題去google。按前人分享的方法一步步操作,bug就一般能解決。

2、和其他人一起寫的代碼

先問清楚其他人最近代碼修改、命名、配置調整、伺服器操作等情況,看看他們給你埋坑沒有,確定沒有之後再去改bug。

三、解決bug

也分兩種情況

1、好代碼

如果你接手的是非常好的代碼,當你修改bug時,發布需要做大量的開發工作,這時候非常有可能解決方案不對了。往往好的代碼產品出現bug,只需要在核心關鍵的地方修改就能完美解決bug。

2、壞代碼

如果你接手的是非常稀爛的代碼,當你修改bug時,千萬別要想著去重構代碼。一定記住:解決bug就萬事大吉了。

PS:友情提醒

【不生氣】修改一個bug往往會引入新的bug,所以改bug要沉住氣不要急,心態要好,不生氣。

【重點】千萬不要在線上產品直接修改bug,哪怕是緊急bug。


這樓挺多答案都是象牙塔的教案。讓我們這種下里巴人來回答一波。修bug是一種修了一萬個bug以後的玄學。我仍然記得2006年有三個星期趕工,修了200bug,到最後,看到測試提的工單我就能感覺出bug的類。

切記我們是工程類,不是科學家。我們做黑盒足夠了,我們徒手足夠了:先看輸入數據是否準確傳到伺服器,再看數據是否準確存入持久層。就這可以80%bug解決。


我一直想著,如果程序員可以寫一段程序,來閱讀代碼發現BUG,如果做到自動修改那就更好了!


在這裡假設你是修復自己寫的代碼的bug.

Debug的難度和你的工作方式密切相關。

想要大幅度降低debug的難度:

勤測試,GIT里多commit. 你每次只改一點,出問題了很容易定位。連改加寫一大坨代碼,跑起來有bug,你花在debug上的時間會指數上升。

Debug的時候,每次只改一處,隔離變動。你所說的越改越煩,估計就是累積改動的結果,越改越程序越偏離期望的行為,還經常改出新bug,最後徹底搞糟,只能推倒重來。

寫防禦性代碼。多寫assert, 尤其是對函數的參數。有些你認為顯而易見不可能發生的情形,也不妨寫一個assert,出bug的時候往往就是你認為不可能的情形發生了,這個概率還挺高。中心思想是如果有問題,越早crash越好。反例是一個不合法的值在程序中傳遞很久,最後在某個地方造成bug, 你debug的時候要一路找回去,經常是程序已經跑了好幾個loop,原來的stack trace都沒了,追蹤費時費力,遠不如一開始就報銷的好。

寫log有它有用的地方,但並不是一個特別有效率的方式。debug level的log寫多了,控制台里動不動一刷好幾頁,從中找出有用的信息會很吃力。


我說下減少bug產生的方法,寫代碼之前不著急動手,先想清楚,把每個分支的可能性在腦子裡過一遍,找張紙簡單畫一下,等徹底想清楚了再寫,bug出現的概率會低很多。新手常出現的問題是改了一個bug又引出另外一個bug,這通常是沒想清楚造成的。

另外就是規範編程習慣,函數不要過長,用函數替換重複代碼,盡量不要出現硬編碼等等,都會有效減少bug的產生。


離開電腦,先在腦子裡過一遍,思考一下,什麼樣得情況下會出這個問題,包括技術上的,業務上的。自己想通了,再去改代碼,驗證自己的分析。而不是不斷的亂改一氣,祈禱會修復。


高票答案說的挺好,沒有抖機靈。

好,下面我就來抖一下。

1、一切空引用異常都是邏輯錯誤!

2、重現bug的概率決定你修復bug的速度

3、要爆炸前,休息3分鐘

4、多使用site:http://stackoverflow.com xxx

5、如果不能調試,就多列印日誌

6、進行專業的測試,追求高的代碼覆蓋率

7、盡量不要使用「世界上最好的編程語言」

8、盡量少寫看起來顯得高深莫測的代碼


鼓弄一會-&>看日誌-&>離開電腦,思考合理解釋-&>嘗試解決-&>沒解決?-&>

^---------------------------回到第一步--------------------------------------|

另外如果確認不是手殘bug+組內氛圍較好的話,可以和同事討論討論。


看到題目中有Java的標籤,我很抱歉,我今天是作為一個Web前端開發者跟你們講,我不是一個Java開發者,但是我見得太多了。我有這個必要告訴你們一些debug的經驗。

1.新手入門時其實挑一個比較好的編輯器/IDE是比較重要的,很多新手會遇到少打了括弧、分號(雖然我大JS寫不寫分號都可以,只要注意一些坑就行了,Py就更不用說了)、拼寫錯誤等等,然後編譯/執行的過程中報一些語法錯誤,其實這些問題一個好的編輯器/IDE都可以提前解決,在寫下代碼的時候編輯器/IDE就會報錯了,這類錯誤最為低級但是剛入門的程序員很容易遇到。經常遇到這種問題的話是時候考慮換個編輯器/IDE防患於未然了。

2.善用平台自帶的調試工具,比如前端開發過程中的瀏覽器控制台,學會基本的打斷點調試,敏感的地方打log調試等等。每一個平台關於調試的文章也是一抓一大把,如果有這方面的問題可以找一些來看看。

3.善用Google等搜索引擎搜索自己遇到的問題或者報錯信息,搜索引擎的使用一般是有技巧的,知乎上也早已有很多討論:

  • 如何用好 Google 等搜索引擎? - Google 搜索

  • 有哪些特殊的搜索引擎? - 搜索引擎

  • 如何高效地使用搜索引擎? - 搜索引擎

至於在國內如何使用Google這裡就不說了,某種程度上可以說這是程序員的必備技能之一。

4.學好英語,善用StackOverflow等技術問答社區,英語不好的可以考慮使用國內的SegmentFault,在達到一定高度之前,一般你遇到的問題都已經有無數人踩過坑了,而其中樂於分享的一些人會通過各種渠道分享自己的解決方案。這裡再給大家一個StackOverflow的討論:

  • 如何優雅地使用 Stack Overflow? - 程序員

5.用好語言/平台/框架/庫的文檔和GitHub的issues還有論壇(前提有,現在一般大多數都有)的搜索功能。有的時候搜索引擎還沒來得及抓取到這些地方的數據,或者是搜索引擎爬蟲因為各種原因壓根就沒抓取,那就需要自己去查了。至於查文檔給大家推薦一個Windows和Linux上的zeal,Mac上可以使用Dash,查找文檔非常方便。

6.寫測試,並使用版本控制例如Git。測試可以將一些隱藏的問題暴露出來(慚愧的是這方面我做的並不好)。養成定期保存版本的習慣,在業務邏輯相對複雜的場景下出錯後藉助Git diff等工具對比可能會有意想不到的效果。

7.通過多寫、多看書、看別人博客等等來積累經驗,開發的經驗其實很大部分就是踩坑後debug的經驗。

在我平時的寫代碼的過程中,文檔+Google其實已經解決了大部分的問題。

暫時想到的就是這麼多,做了一點微小的工作,謝謝大家。


最近改bug機會不要太多。。。我也自己總結一下,希望對你們有幫助。

1,修改之前,理清邏輯,對代碼塊實現功能和預想輸出有個基本概念,這樣大多數時候可以一次定位出錯位置。

2,我以Python為例(其餘不會),我對不信任的代碼塊最常用的就是try/except語句,直接可以定位自己認為出錯概率大的地方,Java是不是用try/catch?

3,分部測試,對於比較大型的代碼塊,每個功能單獨實現後再往主程序上塔,比如實現一個main,裡面定義兩個def,我一般單獨測試def邏輯,測試簡單效果後,才往main上搭,好處就是,步步為營,出錯也容易處理,但是壞處就是效率有點低,而且,得開好幾個文件,有時候包太多會亂,很大部分原因是我技術還不夠好,不知道老司機怎麼處理。

4,善用Google,百度,Stackoverflow這些,特別是最後兩個,不用翻牆,都可以用,Stackoverflow這個真的非常好,建議出錯查看。

5,重啟IDE/電腦/人腦,真的,很管用(?ω?)

6,實在不行,不改了,愛咋咋地,人活著最重要的是開心嘛(?? ?)?


最簡單傻瓜的辦法就是二分法


不是有著名的二分法查bug嗎?

具體的操作步驟:如果你的程序有bug,刪掉其中一半代碼,要是再有bug,再刪一半,直到程序可以正常運行。


推薦閱讀:

怎麼從編程語言的角度解釋kan extension?
為什麼 2010 年前後誕生的語言(如 Golang, Rust, Swift)都是強類型 + 靜態?
python3 為什麼取消了sort方法中的cmp參數?
為什麼C++中會把文件操作抽象為fstream?
為什麼 Python 3.0 設計成不與 Python 2.X 兼容?主要有哪些地方需要突破才導致這一決定?

TAG:程序員 | 軟體開發 | 編程語言 | 編程 | Java |