敏捷的調試

敏捷調試

記錄解決問題的日誌

面對問題並解決它們所開發人員的一種生活方式。當問題發生時,咱們但願趕忙把它解決掉。若是一個熟悉的問題再次發生,咱們會但願記起第一次時如何解決的,並且但願下次可以更快地把它搞定。然而,有時一個問題看起來跟之前遇到的徹底同樣,可是咱們卻不記得說如何修復的了。這種情況時常發生。編程

從網上尋找答案固然賽過僅靠我的努力解決問題,可這是很是耗費時間的過程。有時能夠找到須要的答案,有時出了找到一大堆意見和建議以外,發現不了實質性的解決方案。安全

Don't get burned twice
要想獲得更好的效果,不妨維護一個保存曾遇到的問題以及對應解決方案的日誌,能夠快速搜索之前用過的方法,它叫每日日誌(daylog)服務器

能夠選擇符合需求的任何格式。好比,網絡

  • 問題發生日期
  • 問題簡述
  • 解決方案詳細描述
  • 引用文章或網址,以提供更多細節或相關信息。
  • 任何代碼片斷、設置或對話框的截屏,只要它們說解決方案的一部分,或者能夠幫助更深刻地理解相關細節。框架

  • 要把日誌保存爲可供計算機搜索的格式,就能夠進行關鍵字搜索以快速查找細節。
  • 若是面臨的問題沒法在日誌中找到解決方案,在問題解決以後,要記得立刻將新的細節記錄到日誌中去。
  • 要共享日誌給他人,而不只僅是靠一我的維護。把它放到共享的網絡驅動器中,這樣其餘人也可使用。或者建立一個wiki,並鼓勵其餘開發人員使用和更新其內容。工具

具體技巧

  • 記錄問題的時間不能超過解決問題上花費的時間。要保持輕量級和簡單,沒必要達到對外發布時的質量。
  • 找到之前的解決方法很是關鍵,使用足夠的關鍵字,能夠幫助你在須要的時候發現須要的條目。
  • 若是經過搜索Web,發現沒人曾經遇到一樣的問題,也許搜索的方式有問題。
  • 要記錄發生問題時應用程序、應用框架或平臺的特定版本。一樣的問題在不一樣的平臺或版本上可能表現得不一樣。
  • 要記錄團隊作出一個重要決策的緣由。不然,在6~9個月以後,想再從新回顧決策過程的時候,這些細節就很難再記得了,很容易發生互相指責的情形。

警告就是錯誤

當程序中出現一個編譯錯誤時,編譯器或是構建工具會拒絕產生可執行文件。咱們別無選擇——必需要先修正錯誤,再繼續前行。單元測試

然而警告倒是另一種情況。即便代碼編譯時產生了警告,咱們仍是能夠運行程序。有些警告說過於挑剔的編譯器的良性副產品,有些則不是。測試

可能有人會說優秀的單元測試能夠發現這些問題。說的,可若是編譯器能夠發現這種問題,那爲何不利用它呢?者能夠節省大量的時間和麻煩。要找到一種方式讓編譯器將警告做爲錯誤提示出來。若是編譯器容許調整警告的報告級別,那就把級別調到最高,讓任何警告不能被忽略。設計

在修改設置的時候,要記得在構建服務器上使用的持續集成工具中,修改一樣的設置選項。這個小小的設置,能夠大大提高團隊簽入到源碼控制系統中的代碼質量。調試

具體技巧

  • 雖然這裏探討的主要是編譯語言,解釋型語言一般也有標誌,容許運行時警告。使用相關標誌,而後捕獲輸出,以識別並最終消除警告。
  • 因爲編譯器的bug或是第三方工具或代碼的緣由,有些警告沒法消除。若是確實沒有應對之策的話,就不要再浪費更多時間了。可是相似的情況不多發生。
  • 應該常常指示編譯器:要特別注意別將沒法避免的警告做爲錯誤進行提示,這樣就不用費力去查看全部的提示,以找到真正的錯誤和警告。
  • 棄用的方法被棄用是有緣由的。不要再使用它們了。至少,安排一個迭代來將它們以及它們引發的警告信息安全地移除掉。
  • 若是將過去開發完成的方法標記爲棄用方法,要記錄當前用戶應該採起何種變通之策,以及被棄用的方法將會在什麼時候一塊兒移除。

對問題各個擊破

單元測試帶來的積極效應之一,是它會強迫造成代碼的分層。要保證代碼可測你,就必須把它從周邊代碼中解脫出來。若是代碼依賴其餘模塊,就應該使用mock對象,來把它從其餘模塊中分離開。這樣作不但讓代碼更加健壯,且在發生問題時,也更容易定位來源。

不然發生問題時有可能無從下手。也許能夠先使用調試器,逐行執行代碼,並試圖隔離問題。也許在進入到感興趣的部分以前,要運行多個表單或對話框,這會致使更難發現問題的根源。你會發現本身陷入整個系統之中,徒然增長了壓力,並且下降了工做效率。

大型系統很是複雜——在執行過程當中會有不少因素起做用。從整個系統的角度來解決問題,就很難區分開,哪些細節對要定位的特定問題產生影響,而哪些細節沒有。

答案很清晰:不要試圖立刻了解系統的全部細節。要想認真調試,就必須將有問題的組件或模塊與其餘代碼庫分離開來。若是有單元測試,這個目的就已經達到了。不然,你就得開動腦筋了。

Prototype to isolate.
識別複雜問題的第一步,是將它們分離出來。

但是,不少應用的代碼在編寫時沒有注意到這一點,使得分離變得特別困難。應用到各個構成部分之間會彼此糾結:想把這個部分單獨拿出來,其餘的會緊隨而至。在這些情況下,最好花一些時間把關注的代碼提取出來,並且建立一個可讓其工做的測試環境。

隔離問題不該該只在交付軟件以後才着手。在構建系統原型、調試和測試時,各個擊破的戰略均可以起到幫助做用。

具體技巧

  • 若是將代碼從其運行環境中分離後,問題消失不見了,這有助於隔離問題。
  • 另外一方面,若是將代碼從其運行環境中分離後,問題還在,這也有助於隔離問題。
  • 以二分查找的方式來定位問題時頗有用的。
  • 在向問題發起攻擊以前,先查找你的問題解決日誌。

報告全部異常

從事任何編程工做,都要考慮事物正常情況下是如何運做的。不過更應該想想,當出現問題——也就是事情沒有按計劃進行時,會發生什麼。

在調用別人的代碼時,它也許會拋異常,這時咱們能夠試着對其處理,並從失敗中恢復。固然,要是在用戶沒有意識到的狀況下,能夠恢復並繼續正常處理流程最好不過了。要是不能恢復,應該讓調用代碼的用戶知道,究竟是哪裏出現了問題。

具體技巧

  • 決定由誰來負責處理異常是設計工做的一部分。
  • 不是全部的問題都應該拋出異常。
  • 報告的異常應該在代碼的上下文中有實際意義。
  • 若是代碼中會記錄運行時調試日誌,當捕獲或是拋出異常時,都要記錄日誌信息;這樣作對之後的跟蹤工做頗有幫助。
  • 檢查異常處理起來很麻煩。沒人願意調用拋出31種不一樣檢查異常的方法。這是設計上的問題:要把它解決掉,而不是隨便打個補丁就算了。
  • 要傳播不能處理的異常。

提供有用的錯誤信息

當應用發佈而且在真實世界中獲得使用以後,仍然會發生這樣那樣的問題。當沒法知足用戶需求時,要以優雅的方式進行處理。相似的錯誤發生時,是否是隻要彈出一條優雅且帶有歉意的信息給用戶就足夠了?並不盡然。固然了,顯示通用的信息,告訴用戶發生了問題要好過因爲系統崩潰形成應用執行錯誤的動做,或者直接關閉(用戶會所以感到困惑,並但願知道問題所在)。然而,相似「出錯了」這樣的消息,沒法幫助團隊針對問題作出診斷。用戶在給支持團隊打電話報告問題時,咱們但願他們提供足夠多且好的信息,以幫助儘快識別問題所在。遺憾的是,用很通用的錯誤消息,是沒法提供足夠的數據的。

針對這個問題,經常使用的解決方案是記錄日誌:當發生問題時,讓應用詳細記錄錯誤的相關數據。錯誤日誌最起碼應該以文本文件的形式維護。不過也許能夠發佈到一個系統級別的事件日誌中。可使用工具來瀏覽日誌,產生全部日誌信息的RSS Feed,以及諸如此類的輔助方式。

記錄日誌頗有用,但是單單這樣作是不夠的。開發人員認真分析日誌,能夠獲得須要的數據,但對於不幸的用戶來講起不到任何幫助做用,他們仍是一點頭緒都沒有,不知道本身到底作錯了什麼,應該怎麼作能夠繞過這個錯誤,或者在給技術支持打電話時應該報告什麼。

若是你注意的話,在開發階段就能發現這個問題的早期警告。做爲開發人員,常常要將本身假定爲用戶來測試新功能,要是錯誤信息很難理解或者無助於定位錯誤的話,就能夠想一想真正的用戶和支持團隊,遇到這個問題時會有多麼困難了。

一方面要提供給用戶清晰、易於理解的問題描述和解釋,使他們有可能尋求變通之法。另外一方面還要提供具有關於錯誤的詳細技術細節給用戶,方便開發人員尋找代碼中真正的問題所在。

錯誤報告對於開發人員的生產率,以及最終的支持活動消耗成本,都有很大的影響。在開發過程當中,若是定位和修復問題讓人倍受挫折,就考慮使用更加積極主動的錯誤報告方式吧,調試信息很是寶貴,並且不易得到。不要輕易將其丟棄。

具體技巧

  • 像「沒法找到文件」這樣的錯誤信息,就其自己而言無助於問題的解決。「沒法打開...以供讀取」這樣的信息更有效。
  • 沒有必要等待拋出異常來發現問題。在代碼關鍵點使用斷言以保證一切正常。當斷言失敗時,要提供與異常報告一樣詳細的信息。
  • 在提供更多信息的同時,不要泄露安全信息、我的隱私、商業機密或其餘敏感信息。
  • 提供給用戶的信息能夠包含一個主鍵,以便於在日誌文件或是審覈記錄中定位相關內容。
相關文章
相關標籤/搜索