實戰敏捷開發 Practices of an Agile Developer (5) 除錯篇

Update(2009/9/22): 看到另一篇說明 bug report 該有什麼內容

debugging 最大問題在於,它是一個未知的時間箱子,不知道要花多久才能 debug 完。這章談的是幾個除錯上的技巧。

說到除錯,我也推薦閱讀如何有效地報告錯誤這一篇文章,談的是如何正確回報錯誤,好的錯誤回報可以節省很多時間,而不僅僅只是一句 “程式不會動” 而已。

Keep a Solutions Log (紀錄下解決問題的辦法)

解決問題一直是軟體開發者的工作項目(這裡指的問題比較像是軟體安裝、版本、函式庫使用等等問題,例如 windows 上如何把 MySQL Ruby gem 裝起來等等),不過有時候會碰到似曾相似的問題,疑?我以前是怎麼解決的?拜網路發達之賜,Google 搜尋一下通常會有不少幫助,但是還是得花不少時間找尋解答。

自己維護一份簡單的 solutions log 吧,紀錄下日期、問題描述、解法、參考網址等等資訊,之後碰到類似的問題就可以搜尋的到。將這份 log 用 wiki 維護也是不錯的主意。或是寫在 Blog 上,這樣 google 搞不好還會搜尋到自己以前寫的解法…XD

Warnings Are Really Errors (把警告當真)

編譯器(這裡泛指 complier 或 interpreter) 的警告常常有人是忽略不看的,反正可以編譯執行不會 error 就好了?書上舉了 C++ 的例子,編譯器的警告還是蠻有用的(我想特別是 static 語言)可以幫助你找到跟避免潛在的 Bugs,請不要忽略。請將編譯器的警告也當作程式錯誤或無法通過測試的程式一樣認真處理。

我自己寫 Ruby/Rails 的經驗,最常見的警告像是有:


link_to ( 'login', login_path ) # warning: don't put space before argument parentheses

因為中間多了一個沒用的空白。或是

p Array.new 3, 1 # warning: parenthesize argument(s) for future version

因為 interpreter 覺得 ambiguities 不清楚,這時要請你括號括清楚。另外就是 DEPRECATION WARNING 警告了,告訴你這函式將來的版本將會移除,請不要再用了 :p

Attack Problems in Isolation (隔離除錯)

要在一個大系統找特定問題,不要整個系統一起測,而是將分開隔離找出病因。如果有做 unit tests,我們知道可以將 dependence 的東西用 mock object 隔離出來,這樣測試的時候就不會被外部因素干擾。同理在抓整個系統中的特定 Bugs 的時候,必要的話可以做一些假的 prototype 元件用以隔離抓出問題所在,同時也比較好除錯測試。

我自己土法煉鋼除錯 CSS 也是如此,先移除 CSS 檔案的一半,看看問題是不是出在這裡,然後再砍一半直到找到有問題的那一行。(Binary search?)

Report All Exceptions (回報所有的例外)

如果呼叫一個可能會丟例外的函式,當碰到例外時,盡量可以處理就處理,不然請讓他自然傳播(Propagate)上去給上一層 Caller 處理,不要攔下來又什麼都不做。如果在整個 call stack 中有個傢伙寫了個空個 catch 所有例外,然後 return null 什麼都不做,到時候要找 Bugs 你就會很想殺人了,因為非得 trace 進去 call stack 才能找到原因。

不要覺得自己不會這樣寫,其實常常我們會想”暫時”不想處理這個例外,於是就留下這樣空的例外處理程式碼(然後繼續留到 Production code)。決定要在哪一層處理例外是個設計上的考量,但是如果呼叫一個函式卻要考慮處理二三十種以上的例外可能就有設計上的問題了。

Provide Useful Error Messages (提供使用者有用的錯誤訊息)

一般性的錯誤訊息如 “Something went wrong” 對使用者來說,可說是一點幫助也沒有,既沒有告訴用戶可以怎麼解決,就算使用者想要求助也不知道發生什麼事情。所以盡量提供詳細的訊息告訴使用者發生什麼事情。

當然,如果是比較嚴重的程式例外錯誤,對開發者來說,因為有 logging 的關係,所以看 log 應該可以知道大概的情況並排除,這類的錯誤對使用者來說也沒辦法自行排除,也只能告訴用戶說這不是因為他們操作錯誤的原因。

關於這個議題這一節講的不是很清楚,我個人推薦有本 37 signals 的小書可以翻翻:Defensive Design for the Web 圖文並茂共有40條 Guideline 告訴你如何改進網站的錯誤提示訊息、表單填寫等等,例如:

  • 錯誤訊息要明顯,使用顏色、Icons 等效果
  • 錯誤訊息包含這是什麼錯誤以及如何修復
  • 整個網站用一致的錯誤提示方式
  • 在錯誤提示後,不需要再回上一頁才能修正問題
  • 用易懂、禮貌的字句

實戰敏捷開發 Practices of an Agile Developer (4) 程式篇

趁著新年連續假期,終於把這一章念完了:隨著開發的進展,程式逐漸變得怪獸。這章談幾個重點幫助你讓程式容易了解、擴充及維護。

Program Intently and Expressively (寫清楚了解的程式)

厲害的程式是沒有人可以看懂的程式?程式的可閱讀性比寫得方便還重要,寫只會寫一遍,看卻會看很多次。很多時候會有機會要修 Bugs 或是新增功能,這時搞懂本來的程式常常是一開始最困難的地方,如果一開始寫的人就以可讀性為重要目標,那你就會輕鬆的多。

舉個我自己常見的例子就是 Magic Number,很多人喜歡是一個數字來代表 Type code。例如 1 代表 foo、2 代表 bar、3代表…etc,然後程式裡面就直接寫 1, 2, 3。方便是方便,但是沒多久就會忘記這個 1,2,3 各代表什麼意義。最簡單的解決手法就是請改用字串 constant 處理,或者是用 class 來表示 type。

透過程式語言本身的特色可以寫出更有表達力的程式,函數名稱應該傳達出意圖、參數名稱應該表達出他們的用途,寫出好閱讀的程式我們可以避免很多不必要的註解跟說明文件。一些你覺得明顯簡單的地方,不一定對別人也是如此明顯,甚至是幾個月後的你自己。請想想看幾個月後自己來看還會看得懂嗎?

Communicate in Code (寫出可以閱讀的程式)

我們都討厭寫文件的一大理由是:所謂的文件跟程式碼常是分開的兩碼子事,因此很難讓文件跟的上程式的更新,最終變得難以維護。因此最好的寫文件方式,就是透過程式本身和程式中的註解。

註解是用來寫目的、限制條件跟預期結果等,而不是寫出以下這些程式一行行會做些什麼(請不要拿註解再寫一次程式碼在寫的事情)。一個簡單的判斷方式是:寫在函式 “裡面” 的註解多半是不需要的 (尤其是註解寫 what? 而不是註解 why?)。在重構一書更直言回答哪裡需要重構?那就是有註解的地方:如果程式碼前方有一行註釋,就是在提醒你,可以將這段程式碼替換成一個函式,而且可以在註釋的基礎上給這個函式命名。

你要做的事情應該是讓 source code 本身容易了解,而不是透過註解。透過正確的變數命名、好的空白間隔、邏輯分離清楚的執行路徑等手法,我們很少需要在函式裡面註明這些程式在做什麼,我自己的經驗除非是有參考外部的程式碼或複雜演算法,我會多註明參考來源(網址)。否則沒有必要的註解對我來說就像噪音一樣,妨礙閱讀。

變數跟函式的命名是件非常重要的事情,有意義的命名可以傳達出程式怎麼進行,我舉個 Rails 例子:

p = Post.find(param[:id])
if p
   p.destroy
else
   p = Post.new( param[:post] )
   p.save
end


可以改寫成以下這樣具有清楚意含:

existed_post = Post.find(param[:id])
if existed_post
  existed_post.destroy
else
  new_post = Post.new( param[:post] )
  new_post.save
end

有一種 Job security 的方式就是把程式中所有的變數名稱都代換成亂碼,保證拿到的人超級難看得懂。這就是為什麼手工打造的程式碼有其不可取代的重要性,只要拿到整合型 IDE 像是 Dreamweaver 產生出來的 HTML/CSS code 都很想殺人,因為裡面的變數都是編號產生出來的,根本沒辦法閱讀及擴充。

另外就是大家很喜歡用縮寫,這在我之前寫的文章 物件導向程式的九個體操練習 也有提到,這些只有一個字母的暫時變數並沒有辦法傳達出任何可以幫助了解的資訊。

Class, module, method 前面是個寫註解的好地方,而且每個語言都有工具(例如 RDoc, Javadoc… 等)可以幫忙從程式碼中整理出好看的純文件,這些說明通常有:

  • Purpose 目的
  • Requirements(pre-conditions) 預期的輸入是什麼
  • Promises(post-conditions) 什麼樣的輸入,會有什麼樣的預期輸出
  • Exceptions: 有哪些例外(exceptions)情況

這些說明可以幫助我們由大局觀了解程式是怎麼運作的,而不需要仔細看其中每個 method。
閱讀全文〈實戰敏捷開發 Practices of an Agile Developer (4) 程式篇〉

Ruby Advent 2008 摘要 (下)

Ruby Advent 2008連載完畢,來繼續整理完下集。

  1. GUIfy your Ruby apps with Shoes
    介紹 Shoes 這套有趣的跨平台 GUI 小型開發工具。
  2. Ansuz: Rails based CMS for the Masses
    介紹 Ansuz 這套 Rails-based CMS。裝起來玩了一下,看起來功能蠻多的。不過我個人還是推薦 Radiant 這套比較精簡些。
  3. A Collection of Rack middlewares
    Rack 幾乎要變成 Ruby 社群要做 web framework/apps 標準規格了,Edge Rails 最近在做的 Metal 就是在 Rails 裡也可以撰寫低階的 Rack 操作(效能++)。這篇文章則介紹幾套基於 Rack 的 middleware 函式庫,任何基於 Rack 的 Web framework/apps 都可以使用。也就是對將採用 Rack 的 Rails 來說,這些 Rack middleware 都無需經過 Rails router 和 Action Controller path 就可以把事情做完,真是太厲害了。
  4. Rails 2.2 Internationalization
    介紹及經驗分享 Rails 2.2 的 i18n 功能。有興趣可以看看我之前在 Ruby Tuesday 分享的投影片
  5. Prawn and the future of Ruby PDF generation
    傳統上 Ruby 要產生 PDF 文件,會考慮使用 PDF Writer 等等工具,不過現在有執行速度更快、API設計更好的選擇了(而且 PDF Writer 也不會再出新版了),那就是 Prawn,這是一套 pure Ruby 的 PDF Generator,內建支援 UTF-8,預計2009年中就會推出穩定的正式版。
  6. Full-Cycle Feature Development in Merb
    作者分享他使用 BDD 開發功能的流程:1. 點子 2. 用 Cucumber 寫出規格 3. 用 RSpec 寫 model specs
  7. Ruby Graphs and Java Monitoring with JRuby
    介紹如何使用 RMagick4J,一個 ImageMagick/RMagick 在 JRuby 上的實做品,以及搭配 Gruff 工具。下段介紹 Java Management Extensions (JMX)

  8. Easy Dependency Management for Git with Braid
    如何管理外部程式碼一直是個頭痛的問題,最常見的處理方式就是 copy 一份回來,但是萬一碰到自己有修改,加上這個套件升級了,就會很麻煩。在 Git 流行之前大家推薦用 Piston 來管理,不過現在又多了不少選擇:

    這篇文章介紹 braid 這套管理 vendor dependencies 版本的工具,支援 git 跟 svn。

    其他選擇可以參考這篇 Easy Git External Dependency Management with Giternal 所介紹的有 Tim Dysinger 的 rake tasks、37signal 的 cached_externals、Pat Maddox 的 giternal 等等,也許更適合你。

  9. Jump Start Credit Card Processing
    製作精美 PDF 介紹信用卡金流及 Active Merchant。看起來很棒可是台灣的金流公司都沒有… XD
  10. Dance in the Clouds with PoolParty
    PoolParty 是一套以雲端運算平台為目標的佈署工具,目前支援 Amazon EC2。
  11. HTML Scraping with scRUBYt! for Fun and Profit
    介紹 scRUBYt! 是一套基於 HpricotMechanize 的多功能 web scraping 工具。
  12. Life Beyond Code
    耶誕夜的這最後一篇連載跟技術不太有關係… XD

Ruby Advent 2008 摘要 (上)

Ruby Advent 2008 到耶誕節有一系列的 Ruby 連載:

  1. Simple Blog Engine in Ruby
    介紹 Scanty 這套由 Sinatra 做的 Blog 系統
  2. Deploying Ruby Apps with Passenger
  3. Passenger 其實不只支援 Rails,任何支援 Rack 的 web framework 都可以使用。

  4. Hoptoad for better Exception Notification

    Hoptoad之前有介紹過,是 ThoughtBot 出的網路服務,用來追蹤 Rails 的 exception notifications,最近也剛推出收費版支援 SSL。

  5. Cool as a Cucumber

    Cucumber 是一套基於 Rspec 的 Acceptance Testing Framework。這種驗收測試的作法是用純文字的方式把規格文件(feature documentation)寫出來,然後用這套工具來寫一個可以執行它(parse)它的測試程式。

  6. Machinist for your Test Data Factory

    因為 fixtures 實在不惹人喜愛,所以有些人改用工廠機制,例如 Factory Girl 這套 plugin 可以預先定義好資料形式,方便隨時建立出測試假資料。我的經驗是程式會漂亮不少,但代價是如果假資料量大的話,執行會花不少時間。

    而這裡介紹的 Machinist 也是概念類似的一套實作品(算是 Factory girl 的重新發明)。

  7. SOA with Merb?

    要做 REST calls 目前有幾種 library 可以用 Net::HTTP、RestClient、HTTParty 和 ActiveResource。其中 Net::HTTP 太基本了,而 ActiveReource 太肥不適合搭配 Merb,所以這裡推薦的是 RestClient

  8. Generating (Real) Fake Content

    開發網站的時候,常有要塞滿一些假資料來 Demo 的需求,這裡介紹了 Faker gem 可以產生假文字。

  9. A Simple Distributed Queue in Ruby

    使用 DRb 用九行 Ruby code 就實作出一個 Shared Queue。

  10. Git it done, Rubyists

    介紹 Git 跟一些 Ruby 工具,對 Git 還不太知道的朋友可以看看 Why Git is Better than X (有中文翻譯)

  11. Listening to Your Specs

    這篇長文蠻深入的講解 RSpec 的 BDD(Behavior-Driven Development) coding style。

  12. Thinking out of the Box with Enumerable#inject

    火力展示 Ruby Enumerable#inject 的進階用法,雖然我覺得還挺難懂的。

  13. Meet the Splat

    介紹 Ruby splat operator (也就是asterisk *) 的用法,可以將陣列展開成參數列。

Ruby Tuesday 2008 歲末場 開始報名

各位喜愛 Ruby 的朋友們,Ruby Tuesday 復出首場開始報名囉,詳細資料請前往報名網頁,時間地點是 2008/12/23(二) PM7~9 台北市蛙咖啡。

這次的題目非常難得,godfat 介紹的 Rubinius 和 contagious 介紹的 Merb 都是 Ruby 社群中非常看好其發展的兩套 Open Source Projects,相信可以帶給大家不一樣的視野跟想法。

RESTful Rails 入門開課 (自由軟體技術教學工作坊)

Update(2008/12/12): 上課的網頁在這裡 ihower.tw/course/rails.html,投影片在這裡

感謝 OSSF 自由軟體技術教學工作坊 的邀請,下週四我會在台北開一堂 Ruby on Rails 稍微進階一點的上機實做課程(主辦人不忘提醒我這是推廣教學課程,盡量簡單一點),詳細內容請參考課程介紹

  • 如果從未寫過 Ruby,建議可以看看Ruby Programming Language 網站的 Get Started 部份,或是 Learn to Program,大概了解一下語法。
  • 如果從未接觸過 Ruby on Rails,建議可以一起報名參加另一堂 Ruby on Rails 導覽 (講師不是我,不過內容應該比較簡單)。
  • 如果您有看過、簡單用過 Rails,但是對於 Rails 2 開始採用的 RESTful 設計不明瞭(例如為什麼Rails一代的 scaffold 用的好好的,但是升級二代之後的 scaffold 就看不懂了?為什麼照市面上的Rails書打範例都是地雷沒辦法執行?因為市面上的中文書都太舊了沒講 Rails2 啊。),建議可以來聽聽看。
  • 如果這份投影片的內容您已經會了,就不用來佔人位子囉。