如何高效地增強編程(特別是debug)能力?

現在編程已經是越來越普及。

特別是對於理工出身的人來說,編程幾乎快變成和高數同級的技能。對於已經有一定數理背景的人來說,演算法上的困難比較小,但要寫出bugfree的程序/高效地debug仍然會遇到很多困難。比如,對一個統計出身的人來說,想成為data scientist就必須學好熟練地編程。

這個問題的關鍵就是,放下演算法學習的部分,該如何練習編程這項技能?

我很確信應該更多地練習。但就像體育運動一樣,適當的訓練教程或指南可以讓訓練更加科學高效。不適當的訓練方式則常常有明顯的瓶頸。

歡迎知乎的廣大程序員群體提供指導意見甚至是訓練指南。


這問題很大程度上是個開放性的問題,行業界基本還沒有出現統一的答案。

最關鍵的是注意模塊化。把程序分成小的功能模塊,每個模塊要能單獨拿出來測試。模塊間注意去掉循環依賴,這樣所有模塊會自然分層,遇到問題的時候可以一層一層往下調試。

用IDE和debugger其實有很大功能局限性,更好用的辦法往往是列印log分析log。printf基本是萬能的。

訓練可以這樣。寫一個稍微有點規模的項目,大於2000行代碼的那種,然後嘗試把它分成小模塊,每個模塊一個文件,每個文件不超過300行(或者500行,有的語言可能本身更啰嗦一點),每行不超過80個字元。每個模塊盡量只做一件小事情。注意讓模塊之間沒有循環依賴。給每個模塊寫單元測試。然後,寫出來這個project之後,把模塊之間的依賴關係畫個圖(像這個:Go standard library DAG visualization),拿著這個圖給你的一個(同樣高數沒有問題的)朋友看,讓他參照著這個圖去讀你的源代碼,看他能不能讀懂你的代碼要做什麼,是怎麼做的。如果他能讀懂,說明你模塊分得非常好。如果他讀不懂,可以具體看他讀哪個模塊讀不懂了,研究一下有沒有更好的模塊化方式或者寫法。

如果你的所有代碼都能模塊化成這個水平,那麼你的代碼質量(尤其是在代碼可讀性和可維護性上)已經是世界一流的了。真心的。


Debug這個問題顯然是相當的開放。我相信對於每個程序員都會有自己Debug的具體方法。但是由於Bug這個東西的出現一般相當的詭異,有很多不確定的因素。在找出來之前也不知道具體是什麼原因造成的,所以說可能還是沒有通用的一套方法來找出Bug。那麼我拋個磚,來說說平時我Debug過程中的一些奇技淫巧吧。

首先,從Bug產生的可能情況來看,越是複雜的工程,產生Bug的數量就會越多,而且實際情況是,這兩個之間的關聯是非線性的。所以一定要畏懼代碼的複雜度。對於短小的代碼而言Bug顯然會更容易調通一些。一個非常重要的工作就是進行單元測試。一個非常重要的工作就是進行單元測試。(因為真的很重要,所以我要說兩遍)對於每個單一的函數、模塊、程序等,依次進行測試。去生成一組範圍內的數據(尤其是一些極限情況的數據),保證每個函數、模塊、程序都能夠在任何情況下返回正確的結果。

單元測試的另外一個重要意義是為了防止一種我個人認為Bug中最最噁心的東西。那就是重合Bug。我的意思是,一個程序有多個Bug,他們一起存在在你程序中時,互相發生了奇怪的作用,導致只有某些特定的數據下才會出錯。而當你調通其中一個的時候,你所有數據都出錯了。你下意識就會以為自己之前的修補出了問題。雖然這種情況聽起來非常扯,但是實際上,我經常遇到這樣的問題。

除了單元測試,一個非常重要的技巧是封裝封裝再封裝。把所有可能需要重複調用的東西都封裝成介面、抽象類、方法或者函數。這樣所帶來的好處是不言而喻的。一旦出了問題,可以一次性修改掉所有的代碼。而且調試程序的時候代碼非常短,可以很容易確定出出問題的究竟是哪個部分。

對於具體代碼的調試,我想上面一些答主已經說的很不錯了。最常用的還是列印log的方法。當然,一個擅長去使用一個IDE在開發工程的時候也是必要的。它可以更好幫你跟蹤每個變數在運行過程中是怎麼變化的,當然這些個也是沒有固定的方法的。不用去比較哪個IDE好用,哪個IDE不好用。我覺得對於大多數情況而言,並不需要IDE一些奇奇怪怪的特性支持,能斷點能顯示變數就基本夠用了。

至於很多同學提到可以進一步採用Test Driven Development的方法規避Bug。個人認為這在大工程下是很有效的,但是也相當地反人類。我覺得還是要依據工程的規模和自身團隊的情況進行選擇,或者進行變化。畢竟一個人的命運啊,當然要靠自我奮鬥,但也要考慮歷史的進程。

根據業內多年的經驗,Pair Programming也能大大提高代碼的可靠性。

不過我以前以為Pair Programming是這樣的:

現在據說Pair Programming是這樣的:


如何提高編程能力,這個問題幾乎是媒體們採訪大神們必問的問題。就我所敬仰的一些大神們(Linus,Yukihiro Matsumoto,TJ,多隆等)的回答都會提到多讀源碼,當然每個人還會列出其他的一些內容,但是我覺得讀源碼是十分核心的提升編程能力的方法。(debug其實也是編程能力的一部分,區別可能是需要熟悉一些debug的工具,不過我覺得就題主來說這都是浮雲。編程能力本身是核心。等編程能力上去了,讀代碼的時候讀到不對的地方就會感到硌牙,設計模塊設計不好就會覺得難受,然後debug自然而然就順當多了。。。)

讀高質量的源碼有什麼好處呢? 這和大量的閱讀對於寫作有什麼好處是十分類似的,我想題主作為一個很高閱讀量,文字功夫也非常好的人對此應該也是深有體會的。這種好處難以用條條框框來說清楚。套用文學上的說法那叫語感。寫代碼也有語感。

創作自己的東西之前首先就是閱讀別人創作的東西。多看了高手們是怎麼寫代碼的,試圖去理解高手們寫代碼時在想什麼,久而久之(其實也不用太久,兩個禮拜細心的讀代碼就能讓能力上升一大截。)自己就會模仿就會慢慢地學會如何像大神一樣寫代碼。等閱讀量和寫作量積累到一定程度,很多業界所謂的設計規範,模塊流程會自然而然的理解並遵照執行,因為這已成習慣。(其實沒說的那麼自然啦,據傳TJ大神是純看代碼流。[TJ Holowaychuk"s answer to How did TJ Holowaychuk learn to program?] 正常人會看些書同步補充些規範啊之類的相關知識來幫助看懂代碼。)相反如果一開始強套規範,容易遇到瓶頸,那時就只好說,臣妾做不到啊。

----------------------------------------------------2/15更新-

我覺得適合(以學習目的)閱讀的代碼有一下幾個特點。

1。高質量。比如千錘百鍊的基礎庫。

2。上下文環境熟悉。在讀一個函數或組件的代碼之前應該用過它,最好是經常用它,對於它的用法十分熟悉。恩,這也是我首先推薦閱讀基礎組件庫的原因,因為這些總是經常用到的。

3。不涉及過於專業的知識。基礎庫很多代碼並不適合這一條,特別是為了性能考量,代碼中會涉及一些比較難的演算法和罕見的處理手段。所幸的是這種東西出來的時候一般都會帶上大把注釋。開始時候迴避一下,後來還是很值得讀一讀的。是在看不懂的話多讀一兩篇paper也能搞定,一般注釋中都給出線索的。(當然了,有些就變成迷了,比如這段非常有名的代碼,就只是留了個很邪惡的注釋。 Fast inverse square root。)

讀了代碼以後可以做的兩件事。

1。 默寫。讀完代碼以後等感覺基本把細節忘乾淨了,比如一周到一個月後。像我這種小短腦三天就夠了。。。自己把閱讀的代碼實現一遍。如果實現不下去了,或者覺得自己實現得很醜,就看看原版代碼。

我自己在實踐過程中最常碰到的就是模塊設計上的障礙,核心的演算法往往記得,數據結構也大致記得,但是模塊是怎麼設計的就忘得一乾二淨了。寫起東西來總是磕磕絆絆的,然後再回頭去看「正確答案」,會對別人的設計理解更深一層,發現在美好的設計下那些磕磕絆絆都自然消失了。或者忘了其他什麼部分,總之忘了什麼就說明缺什麼,回頭看一下大師的代碼就能趕快補上。

2。翻譯。現在很多開源項目的項目介紹中都會寫「inspired by balabala」,基本是說我看了某某項目的源碼(語言A),然後依著他的框架結構功能在語言B里實現了一套。這件事情其實是十分好的鍛煉編程能力的方法,演算法、邏輯、設計別人都已經做好了,需要做的只是遷移。遷移的過程中需要對原項目的代碼結構有清晰的認識,還需要在現項目中對應的實現,但是對於原演算法的理解和一些其他業務性的考量要求卻不高(畢竟只要抄就好了)。如此一來,幾乎就是集中鍛煉了純純地編碼能力。

-------------------------------------------------------

關於debug,我覺得這其實是小事,還有那麼多人還在用printf debug呢! 但是也是很煩惱的一件事,很多時候總是覺得debug很不順手。這裡也說一下我的看法。

debug首先是編程能力的一部分,如果都搞不太清楚程序再幹什麼又怎麼debug呢?如果模塊設計很差,演算法實現混亂,出錯的概率和debug的難度當然會增加。相反,設計的好,編程習慣好,想出錯都不容易。

debug主要有三種工具,debugger(mike zhang 給出的 26.2. pdb — The Python Debugger 是一個很好的debugger工具。),log(print大法),assertion testcase (可以視為一種工具化再抽象的print大法,其實本質和log沒區別)。

可以從這樣一個視角去看這三種工具:

工具: debugger——log————assertion

可操作度(對人靈活性): 高————中————低

表述能力(對機器靈活性):低————中————高

請按需取用,對於離機器端較遠,邏輯抽象度較高的應用,一般會更看重debug時對內部狀態的表述能力而不是具體的運行細節。相反離機器端比較近的,邏輯抽象度並不高的場景,一般更喜歡用debug一步一步跟,希望能了解程序具體的運行細節。

以上僅僅是我個人的理解,實踐出真知。各種debug方法都用一下,然後再判斷該怎麼debug吧。況且實際使用時各類工具是可以混合使用的。

另外當程序規模漸漸超出一個程序員能力範圍的時候,debug有時的確是十分苦惱的,大神也不例外,他們也會抱怨,然後說「我不想再寫XXX了」。例如:

Why should I have written ZeroMQ in C, not C++ (part I)

Farewell Node.js

(記得linus也吐槽過linux kernel變得越來越大而越來越難以維護和調試,文章我找不到了。如果有線索的童鞋希望能告知一下。)

所以當覺得調試很難時請不要灰心。那些說著不幹了的大神們後來還是都回到了自己原來的位置。困難總是一時的。

------------------------------------------------------

對於python來說有一步很重要的debug步驟靜態類型檢查不是python環境自帶的。它可以在運行期之前就解析並探知python代碼中的類型錯誤,十分有用。可以減少很多的麻煩。

現在比較好的工具如下。

Sourcegraph

https://github.com/yinwang0/pysonar2

------------------------------------------------------

然後就如同教別人怎麼讀書一下,現在應該寫推薦書目了:

先弱弱的問一句除了python還會什麼?一般而言讀源碼我會先推薦別人當前語言的標準庫,先從二分搜索和排序開始看起。這兩個演算法幾乎每個語言的標準庫中都有,它們是將邏輯本質從事物本身表象中解耦的典範,而且除了語言本身以外它們對其他知識和上下文語義的依賴很小,十分適合作為最早的讀物。很可惜的是python的這部分標準庫是C寫的,所以並不能靠讀這些立桿見影地提高python的編碼能力。(當然那些C代碼也很有價值,特別是python的排序實現,很精彩,後來被java啊,octave啊,gnome啊都抄了去。其實編程能力總是相通的,讀了對python還是很有好處的。)入門"書籍"都不知道推薦什麼,真讓人團團轉。(我猜其實題主既然是用python在做機器學習,應該底層實際接觸的還是C。其實cpython的標準庫也可以讀~~)

恩,推薦"書目"之前我能問一下題主的編程水平嗎?

比如寫過多少代碼。然後看看這裡Problems | LeetCode OJ的題目,用python做一下感覺如何?(這裡的題目也很好,做做也是很好的提升代碼能力的途徑。)描述一下,然後我幫你找找看合適的閱讀材料。其實我覺得吶你只要雙手一攤問你導師要他的代碼或許就可以了,雖然程序寫的最漂亮的還是那些大師級的程序員。

啊對了,光讀不練是不行的,如果追求高強度,高效,那就請刷題吧。題主坐擁世界上最好的計算機教育資源,我這裡就不獻寶了。

就把碼代碼當做寫作,我相信題主你可以的!

========================================

為表對cpython TimSort 的景仰還是貼點連接:

cpython: 99302634d756 Objects/listobject.c

http://bugs.python.org/file4451/timsort.txt

TJ 是node社區代碼貢獻第一的男人,代碼質量高產量高。以下是他回答他是如何學習編程的。

TJ Holowaychuk"s answer to How did TJ Holowaychuk learn to program?


這個問題一定要回答呀!每個程序員都有在debug過程中被虐得很慘的經歷,但是沒有虐情哪來進步呢,我可以談談我的一些建議吧~希望每個程序員都能被溫柔對待~

首先,debug一定要靠自己呀!

原因有四:

1. 如果是別人給你指出你的程序哪兒錯了,你自己不會有任何收穫,你下一次依舊會犯同樣的錯誤。
2. 經過長時間努力Debug 獲得的錯誤,印象更深刻。
3. Debug 能力是面試的考察範圍。
4. 鍛煉Debug 能力能夠提高自己的Bug Free的能力。

其次,我想介紹一下debug的基本步驟:

1. 重新讀一遍程序。按照自己當初想的思路,走一遍程序,看看程序是不是按照自己的思路在走。(因為很多時候,你寫著寫著就忘了很多事兒)這種方式是最有效最快速的 Debug 方式。
2. 找到一個非常小非常小的可以讓你的程序出錯的數據。比如空數組,空串,1-5個數的數組,一個字元的字元串。
3. 在程序的若干位置輸出一些中間結果。比如排序之後輸出一下,看看是不是真的按照你所想的順序排序的。這樣可以定位到程序出錯的部分。
4. 定位了出錯的部分之後,查看自己的程序該部分的邏輯是否有錯。
在第4步中,如果無法通過肉眼看出錯誤的部分,就一步步「模擬執行」程序,找出錯誤。

最後,要是debug還是不行怎麼辦?

如果已經 Debug 了一整天,還是不行,那麼兄弟,你可以考慮向他人求助了~

能力提高是一個循序漸進的過程,這就給自己定個小目標:做一個debug的小能手~

歡迎關注我的微信公眾號:ninechapter


print大法好。把斷點結果打出來,什麼原因造成的不對一下就知道了,IDE的debug的作用要在自行debug基礎成熟後才能體現出來,要不然IDE光說你這行錯了,你還是不知道怎麼改合理。

在寫工作代碼之前先寫好單元測試:這樣可以縮小debug的範圍,練習怎麼寫test cases


debug 的話,思路基本上就是不斷地問自己兩個問題:出了什麼問題?在哪兒出的問題?前一個問題要求你能精確地說出期望的行為或結果以及實際的行為和結果,後一個問題則要求精確到某一行某一個語句。這樣不斷追根溯源,直到問題解決。

某些並發問題可能要更倚重經驗一些。

但從來另一方面講,debug 雖然重要,但僅僅 debug 強不算是好的程序員。應該一次寫完就能運行。要減少 debug 上浪費的時間,個人經驗:

  1. 設計階段就要想好各個模塊、對象間的關係和交互,控制、數據如何流動等重要細節。如果有模式或者最佳實踐可用,就用上。
  2. 測試先行。起碼把最重要的單元或模塊的測試寫出來。
  3. 從一開始就要考慮各種出錯的情況,如 I/O 錯誤,外部模塊錯誤,用戶誤操作等等。我帶的本科生中很多新手不喜歡考慮出錯的情況,結果導致程序脆弱,極易崩潰。
  4. 注重代碼質量,不要寫看著就亂糟糟的代碼,不要用 asdf 這樣的命名。
  5. 對某些語言,比如 C++ 之類,要知道常見的陷阱,遵循一定的規範,比如使用裸指針時要搞清楚所有者,所有者負責分配空間和回收等等。
  6. 學會一個簡單的日誌庫,或者直接自己手寫一個。print 大法不錯,但我發現很多情況下日誌里的其他信息,比如時間、文件名、行號、線程ID等還是很有用的。這時即使是手工定義的一個 LOG 宏也比 printf 要方便。

暫時就想到這麼多。


嚴格按照『debug 作業規範』來做即可

『debug 作業規範』具體是怎樣的就比較麻煩了,我手中正好有一段幫人演示 debug 的視頻,上傳到了 B 站,也順便貼到了知乎(下方視頻)

裡面大概有 4、5 個不同種類的 bug,我 debug 的同時還解釋了一下思路,對你不一定有幫助,不過好在只有 4 分鐘

如何更好地 debug - 嗶哩 (゜-゜)つロ 乾杯~-bilibili


補充一下思路.

熟悉和精通調試工具, 比如對於前端來說, 看 Google DevTools Team 的演講, 追團隊成員的, 盡量熟悉調試工具的新特性和隱藏功能, 非常有助於理解他們希望我們怎樣去調試代碼, 從中能學到很多技巧.

學習類型系統. 編譯器是可以通過類型系統來排查代碼當中的錯誤的, 如果寫的是強類型靜態類型語言, IDE 提供類型上的支持應該就有的. 即便動態語言, 通過 assert, 或者語言提供的靜態檢查(類似 ESLint 吧)也能去掉很多的錯誤.

擁抱不可變數據. 代碼當中有一部分報錯是意外的數據修改導致的, 由於可變數據沒法很好反應在調試工具里, 追蹤這類 bug 就需要大量的 Log 來顯示狀態. 不可變數據結合全局的狀態樹能大幅控制這類 bug, 數據本身都不可變, 只有狀態的地方會改變, 就很難因為可變而出現 bug 了.

以及其他良好的工程習慣, 甚至好的編程語言.


服務端和客戶端,各種語言的技巧都是不同的


我就說一下程序員老手 Debug 的套路吧。

大家都知道,程序員是寫代碼的。但是現實情況卻是,寫代碼僅僅佔了程序員日常工作的一小部分,大部分時間都在幹嘛? 解 Bug !

當然也有例外,比如初創公司的起始階段,比如一個全新的項目從無到有的過程,這些階段是程序員集中編碼階段,在 Coding 上花費的時間大概與 Debug 的時間相當,或者略大於 Debug 的時間。

很多的公司,尤其是大公司,都有著非常成熟的產品,除了一些全新加嶄新的項目,程序員很少需要把軟體項目從 0 做起,都是在之前的版本上加以改進,完善和迭代,同時還需要對老版本進行維護。

所以,解 Bug ,很多時候,成了我們工作的重中之重。能不能快速地定位 Bug,解決 Bug,也是檢驗程序員新手與老手的一種方式。

那麼,程序員老手是怎麼解 Bug 呢?

其實都是有套路的,且聽我慢慢道來。

經驗預判

測試人員報過來一個 Bug, 首先可以根據經驗判斷一下,這到底是不是真的是一個 Bug。

千萬不要相信,測試說有一個 Bug,這真的就一定是個 Bug。

根據測試的 「Bug」 描述,先看一下:

1. 是不是測試用的軟體版本不一致?因為有的時候,這個 Bug 已經在代碼庫中 Fix 了,但是測試人員並沒有及時獲取到這個信息,用的還是老的軟體版本。

2. 是不是測試工具的問題?遇到結果不對,並不一定是你軟體的問題,有的時候還有可能是測試工具的問題。

3. 是不是測試環境沒有設置對?測試環境的軟硬體是否匹配?軟體版本與測試工具之間是否匹配?環境變數與測試腳本設置對了沒有?測試用例用的對不對?

4. 是不是一個已知而又暫時不好解決的 Bug?比如這個 Bug 跟一個已知的硬體 Bug 相關,而這個硬體 Bug 一時半會也解決不了。

5. 是不是以前也遇到過類似的 Bug ? 而針對這個 Bug,已經有了解決方案,但是還沒來及提交進代碼庫。

6. 是不是別的開發人員已經遇到過這個問題? 可以適當詢問一下比自己有經驗的同事,以前有沒有遇到過這個 Bug。說不定這就是個別人已經踩過的坑,那麼知道了這個信息,你就沒有必要把這個坑再踩一遍。

猜測解決方案

這一點其實還是屬於經驗預判。

當你確定這真的是一個 Bug,而根據 Bug 的描述,又可以八九不離十猜測出 Bug 的出錯原因的時候,可以試著修改代碼,把修改後的代碼讓測試人員拿去測試,看一下能否解決這個 Bug。

這一招是用在你對這個修改方案很有信心的時候。可以避免本地復現 Bug 的時間和精力消耗,因為有的時候,搭建個能夠復現 Bug 的環境,還是很繁瑣的。

但是,切忌把你胡亂猜測的修改拿去讓測試人員測試,這一點對測試人員也不負責任。

本地復現 Bug

憑著自己的經驗,沒法一眼看出問題所在,那麼就只有動手先把 Bug 在本地復現了再說吧。

前面說了,復現 Bug,搭建測試環境,有的時候是一個很繁瑣的過程。涉及到軟體、硬體的匹配,測試工具的匹配,測試用例的匹配,還有各種環境的配置都要匹配。

所以這也是為什麼我建議大家,不要一上來就急著搭建測試環境,復現 Bug,可以憑自己的經驗先行判斷的原因。當然,一些簡單的很好復現 Bug 的場合除外。

復現 Bug, 除了搭建測試環境比較繁瑣以外,還有一個很令人頭疼的地方,那就是隨機性。

同樣的測試環境,只有在測試人員機器上 Bug 才會出現,而在你機器中,就是好的,你說是不是很邪門? 你還別不相信,這種情況真的時有發生,鬼知道 Bug 經歷了些什麼!

隨機性 Bug,還有一個噁心的地方,就是不是 100% 可以重現。有的時候測試用例跑個 100 遍,Bug 才出現那麼一兩次,程序員守在電腦前,等待個 Bug 重現,像是等待彩票開獎一樣,這心情,只有經歷過的人才懂。

前面說這麼多,只是想說明,復現 Bug 有的時候,是多麼繁瑣,多麼痛苦。

開始調試

假如有幸復現了 Bug,那麼接下來進入正經調試階段。

1. 查看 Log。大型軟體項目裡面,都會有一些調試信息,會生成一些 Log,可以首先查看一下 Log 裡面有沒有錯誤信息,這是排錯最直觀的方法。

2. 用調試工具單步跟蹤代碼進行調試。看一下程序在運行的時候,代碼裡面發生了什麼,跟蹤一下代碼邏輯執行步驟,有沒有發生邏輯錯誤,是不是執行到了異常代碼處理區,函數輸入和輸出值是多少,臨時變數,全局變數,結構體的值又分別是多少。

3. 列印程序輸入、輸出參數、臨時變數,緩衝區和內存空間。把這些變數存在文件中,用 Notepad++ 查看一下,是不是跟預期的一致,有沒有明顯錯誤的地方,需要的話,再進一步查看它們的十六進位碼。

4. 在任意需要的地方添加列印。有的時候,單步跟蹤不是很方便的情況下,或者在多線程情況下怕影響時序,可以在代碼裡面逐行添加列印,把你想要的東西列印在文件里,這樣方便查看。

修改代碼

經過前面的調試,你一定有了值得懷疑的目標。接下來嘗試修改你懷疑的代碼段。

這個過程不是一蹴而就的,需要經過不斷的修改、添加、刪除代碼。

1. 改正要害。經過調試,假如你已經確定了錯誤的原因,那麼可以直擊要害,一下改正。

2. 增刪代碼。只是懷疑,還不確定,可以嘗試一下把你懷疑的代碼段注釋掉,看一下是不是就好了,接下來,進一步縮小注釋範圍,直到聚焦到某一行代碼,那麼這一行代碼應該就是導致 Bug 的原因。但是這種還是比較理想的情況,有些問題比較複雜,你可以自己手動添加一些便於調試的代碼進去。

這一過程本身需要不斷嘗試,改著改著,試著試著,就發現問題所在了。

解決問題

假如說前面的一切工作是為了發現問題,那麼接下要做的是解決問題。

個人感覺解 Bug 的過程,最難的不是怎麼解決問題,Fix Bug,而是前面定位 Bug ,找 Bug 的過程。可以說,找到了 Bug,解決方案就呼之欲出了。

定位到了 Bug 的原因,能自己解決的就自己解決,自己解決不了的申請資源,讓相關人員 (stakeholder)跟自己共同解決。

當然,還是有些情況,發現了問題所在,Bug 並沒有那麼好解決。

最後,修改了代碼,解決了 Bug,在 commit 之前,別忘記多測幾遍,看一下這個 Bug 是不是真沒有了,再做一遍回歸測試,看一看會不會引起新的 Bug。

多做幾遍本地測試,嗯!

做完測試,就開始走流程,提交代碼吧。

提交完代碼,老天保佑,集成測試不會有問題,先讓我忐忑幾天。

我能想到的就這些,大家可以補充。


老王家debug三板斧~:

1、print——通過這個函數輸出函數中值得懷疑的變數的值。對於小程序和單元測試很有效果。事實上多數程序用這個方法就很ok啦~

2、查看console輸出的報錯信息——比如NullPointerException,一定是某個變數沒有賦值,然後定位到exception出現的位置。

3、IDE自帶的debug功能——主要是查看一些複雜數據結構的賦值。

您發現沒有,看了半天,我們多數情況下都是查看某個變數的賦值情況而已。


我總結的16字訣:明辨職責,釐清結構;規範注釋,善用斷言。

明辨職責:不管是用什麼程序設計方式,都存在代碼塊。每個代碼塊的職責必須是清晰不變的、可以明確定義的,並且在邏輯上是完整的、完備的。如果職責不清晰,那麼程序的結構一定是混亂的。這對程序的開發及調試是極大的困擾。

釐清結構
:程序設計時,思路一定要清晰,首先就是層次結構要明確,這樣才能弄清楚代碼塊之間的關係,使得代碼塊組成的結構也更趨於合理。做一件事,通常會分解為多個大步驟,每個大步驟又可以分解為多個小步驟,小步驟還可以繼續分解,這樣的結構就是層次結構。最終代碼塊所屬的步驟在什麼層上一定是清晰的,如果這個層次被打亂了,那麼程序出現bug的概率極大。

規範注釋
:很多時候,程序bug的產生是因為內部某些隱式條件的不滿足,這更多的體現為調用者並不清楚被調用者對他的要求。因此,不要吝嗇注釋,通過規範的注釋把這些要求表達清楚,會使得bug顯著減少。

善用斷言
:注釋是防「君子」不防「小人」,對於不遵守規則的調用者,需要有防備,因此,通過斷言(或者檢查判斷)來保證所要求的前提已被滿足也很重要。另外,自己的代碼塊執行完畢後是否達到預期的目的,也可以通過斷言或檢查邏輯來判斷。這方面,契約式編程的一些思想和做法可以借鑒。

以上先寫這麼多,有空再來補充。


我覺得要分情況討論吧。。

如果是只跑幾次、寫完即棄的程序(搞Data Science的經常是這種 ),那主要就是靠熟能生巧,以及其他樓提到的善用debugger。另外有bug的程序還不一定會跑崩,還有可能會效率下降或者最終效果(比如分類器的準確率)下降了若干個百分點但還能用,這種bug沒法靠運行發現,只能靠自己靜態檢查代碼。

如果是需要長時間維護的程序的話,那應該像搞工程那樣,測試先行+借鑒設計模式。


首先是寫測試,尤其是 TDD 最為重要。 TDD
帶來最大好處的一點不在於測試,而在於強迫你考慮設計是怎麼樣的。寫測試的時候雖然還沒有實現,但卻要求你思考該有哪些介面,這些介面該怎麼用、行為如
何。當你工作在一個較高的層次上的時候, TDD 幫助你設計一個比較好的 SDK 介面;當你工作在某個底層的模塊時, TDD
幫你思考它和其它模塊是如何交互的。至於 TDD 帶來的「副產品」——測試——在下次某個「倒霉人士」接手這塊代碼的時候就能排上用場了

然後是邏輯思考的能力。在運行之前先 reason with the program ,絕大多數的 bug 在這一遍就能發現出來。最有用的就是小黃鴨調試法 http://en.wikipedia.org/wiki/Rubber_duck_debugging ,找不到小黃鴨用真人也行。重要的是對任何細節不要想當然,要講出為什麼這麼做,這麼做可能會帶來哪些影響。過完整個程序,可能的問題也就不言自現了。

最後是編碼的能力。之前為了強化 C++ ,用記事本寫過一陣子,後來對於語法細節和 STL 一類常用庫的 API 都爛熟於心,就算 VS2012
之後的 IntelliSense 總是卡頓也不會降低效率。這樣寫代碼的時候就不會總是因為 template template class
member function specification 這類問題在 Google 上花費半天了。

每個人的所長會各有不同,但有
意識的在非舒適區聯繫特別重要:總是有邏輯上的 bug ,那就試試 reason with the program
;要是對語法或API不熟,總離不開 Google ,那就試試記事本;你是個 TDD 黑,那我也沒辦法,但至少大家都還是寫測試對吧?


向下翻了好長時間,才看到有一個聊TDD的,TDD真的是一個減少bug的非常棒的模式


近幾年工作中,帶過很多剛畢業的新人上崗,這個我總結一下無非是:

多看,多寫,多想。

多看,看別人寫的代碼,好壞代碼都看,在看了一段時間以後,你自己知道什麼代碼叫做好代碼,什麼樣的代碼會讓你不斷吐槽。

多寫,代碼如文章。從來沒有一個文豪是看文章看出來的,寫是必須的一個過程,也是很漫長的一個過程,在有清晰的概念來定義代碼的好壞的時候,你需要通過不斷的練習讓自己的語言表達能力過關。

多想,綜合上面兩點,多動動腦子,不要別人說什麼就信什麼。


認真學習ide,例如vs2012,code::blocks,pycharm等,根據實際情況選擇,熟練使用一款ide之後就差不多了,ide本身就提供了強大的debug功能。


寫Log是很有用的了,如果你模塊化做得好就馬上能斷定出位置,然後Debug就能高效了


只是給個建議。對於不太龐大的程序,試試看把代碼列印出來,不藉助debug而是看代碼查錯。堅持一段時間將會有很大提高。

還可以選擇手寫代碼/不藉助ide的無高亮無補全的寫代碼。當你能夠做到寫出來沒問題的時候,bug一般都會很少。


要熟悉調試器的使用,條件斷點啦 watch 啦

printf雖然簡單 但有的程序編譯一遍就很費勁


推薦閱讀:

二本院校在上海找得到軟體開發實習嗎?
學生團隊實施scrum敏捷開發的疑問?
qt5.4以後會向什麼方向發展?
黑客可以厲害到什麼程度?
關於軟體測試和軟體測試工程師有什麼好笑的段子?

TAG:編程 | 軟體工程 | 計算機科學 | 軟體調試 |