【翻譯】程式碼審查經驗談

,發表於2014-10-12

原文: http://blog.salsitasoft.com/practical-lessons-in-peer-code-review/

百萬年前,類人猿從樹上下來,進化出了對生拇指......最終......成為了人類。

我們可以從同樣的角度看待納入工作流程的程式碼審查工作:它將人類與混跡於連綿起伏的軟體開發大草原上的獸類區分開來。

盡然如此,我還是有時會聽到團隊成員有類似於下面這樣的評論:

  • “在這個專案上進行程式碼審查就是浪費時間。”
  • “我沒有時間做程式碼審查。”
  • “釋出時間延遲了,因為我的膽小怕事的同事還沒有做完程式碼審查。”
  • “你能想象的到嗎?我的同事居然想讓我修改我的程式碼!請告訴他們,如果我的完美無瑕、精緻無比的程式碼做出了任何改動,整個宇宙的和諧都會遭到破壞!”

為什麼要做程式碼審查?

首先,我們要牢記我們為什麼要做程式碼審查。任何一名專業軟體開發者的最重要的目標之一就是持續不斷地提升他們的作品的質量。即便你所在的團隊全都是由明星工程師組成的,除非你能與大家團結合作,否則與一個能幹的自由職業者並沒有什麼差別。程式碼審查就是達成這一目標的最重要的途徑之一。具體說來,程式碼審查可以提供如下好處:

  • 多了另外一雙眼睛來發現缺陷與更好的實現方法;
  • 確保至少有另外一個人熟悉你寫的程式碼;
  • 通過接觸更有經驗的人寫的程式碼,可以培養新人;
  • 審查者和被審查者通過接觸對方好的想法及實踐方法,可以促進知識共享;
  • 知道程式碼要經過他人的審查,督促開發者更周密地完成他們的工作。

進行周密的審查

然而,除非投入足夠多的時間和精力,否則這些目標難以達成。僅僅是瀏覽一段補丁程式碼,確保使用了正確的縮排以及所有的變數都使用了小駝峰式命名法,並不算作周密的審查。我們可以參考結對程式設計——一項非常流行的實踐活動,需要多一倍的開發時間來完成——以此來作為程式碼審查需要付諸的工作量的基準。雖然程式碼審查需要投入很多時間,但是與結對程式設計相比,還是要少很多。

我的感覺是,應該把原來25%的開發時間用在做程式碼審查上面。比如,一名開發者花費了兩天的時間完成了一項任務,那麼審查者就應該使用大約四個小時的時間來審查這部分程式碼。

當然了,重要的不是花費了多少時間,而是程式碼審查是否被正確地執行了。具體說來就是,你必須搞懂你審查的這段程式碼。這並不意味著你一定懂得編寫這段程式碼使用的語言的語法,而是要搞明白它是如何被構造進整個應用、元件或者類庫的。如果你沒有弄清楚每一行程式碼的含義,那麼這樣的審查是沒有多大價值的。這也說明了為什麼好的程式碼審查不能快速進行:探究觸發特定函式的多個程式碼路徑,確保第三方類庫API被正確的使用(包括任何極端情況)等等都是需要花費很多時間的。

除了在審查的程式碼中尋找缺陷或者其他問題之外,還要確保:

  • 包含所有必要的測試用例;
  • 有合適的設計文件。

即便是擅長寫測試用例和設計文件的開發者也常常會在程式碼做出改動時忘記更新測試用例和設計文件。適當時機來自於程式碼審查者的小小的提醒對防止它們隨著時間的推移而失效至關重要。

防止過度的程式碼審查

如果你的團隊將程式碼審查納入工作流程,那麼,一個潛在的危險是積壓的程式碼審查工作有可能達到失控的程度。如果你有兩週的時間沒有進行程式碼審查工作了,那麼彌補這些工作會很輕鬆地花掉你幾天的時間。這也就意味著當你下定決心去完成這些工作(程式碼審查)時,你自身的開發工作會遭受意外的重創。因為合適的程式碼審查工作需要持續的高強度的腦力活動,這樣也會使做好程式碼審查工作更加困難。連續幾天不間斷地進行這項工作是相當有難度的。

由於上述原因,開發者應該努力做到“日清”。一個方法是將其作為上午第一件事情來做。在開始自己的開發工作之前將待完成的程式碼審查工作處理完,可以防止其處於失控狀態。有些人會選擇在午休前或後或者下午下班之前進行這項工作。無論在什麼時間做這件事情,把它看做是一項日常例行工作而非分散注意力的事情,可以讓你防止:

  • 沒有時間處理程式碼審查待辦事項;
  • 由於尚未完成審查工作,要延遲程式碼的釋出;
  • 由於在審查的同時程式碼發生了很多變動,而導致審查無效;
  • 不得不在最後一刻匆忙結束這項工作導致審查效果很差。

編寫可審查的程式碼

失控的待處理審查工作並不總是由審查者導致的。如果一位同事花費了一週的時間毫無條理地新增了貫穿一個大型專案的程式碼,這樣釋出的補丁程式碼確實是非常難以審查的。這樣就要求在一次審查工作中完成太多東西。弄清楚這些程式碼的意圖和大體結構的難度可想而知。

這也是為什麼要把工作劃分成可控的小單元的原因之一。我們採用的是scrum方法,這樣適宜的單元就是使用者故事。努力按照使用者故事來組織工作,只提交與你目前正在著手進行的使用者故事相關的程式碼,這樣寫出的程式碼會使審查工作非常輕鬆。你的團隊可能採用的是別的方法,但原理是一樣的。

能夠編寫可審查的程式碼還有其他先決條件。在做出有技巧性的架構級決策時,事先跟程式碼審查者進行討論是很有意義的。這樣做會使審查者更加容易搞懂你的程式碼,因為他們知道你要達成什麼目標,以及你計劃怎麼做。這樣也會有助於避免審查者提出了其他更好的方案,而你不得不重寫大片大片程式碼的情況。

專案架構資訊應該詳細地記錄在你的設計文件中。這一點很重要,至少它可以幫助新人熟悉瞭解新專案的詳細情況和理解現有的程式碼。另外一個好處就是幫助審查者做好他們的工作。單元測試用例也可以幫助審查者瞭解元件應該如何使用。

如果在補丁程式碼中包含了第三方的程式碼,要單獨提交。有9000行的jQuery程式碼橫在當中,會使得程式碼審查工作很難進行下去。

要編寫可審查的程式碼,非常重要的一步是,在自我審查過程中做標註。也就是,自己審查一遍程式碼,在認為有助於其他審查者更好地理解程式碼的地方新增註解。我發現給程式碼做標註會花費相對較少的時間(通常是幾分鐘的事),卻使程式碼審查工作的速度和質量有很大的提升。當然,程式碼註釋也會帶來同樣的好處,而且應該在適當的地方新增,但是通常審查標註更有意義。一個額外的好訊息是,研究表明,開發者會在閱讀自己的程式碼並做標註的過程中發現很多缺陷。

大規模程式碼重構

有時會需要重構一個影響很多部分的程式碼庫。對於大型的應用來說,這通常會花費幾天(甚至更多)的時間,結果是一個巨大的補丁。這種情況下,常規的程式碼審查工作變得不太現實。

最好的方式是採用漸進的方式重構程式碼。設法劃定一個合理的範圍進行區域性修改,這步修改要讓程式碼庫能夠執行,並且與你的最終方向保持一致。在這步修改完成並且通過審查之後,進行第二步漸進式修改,如此迴圈往復,直到整個重構工作完成。採用這種方式或許並不總是能實現的,但是經過周密的思考和計劃,在重構過程中避免大量的大塊的補丁通常是可以實現的。這種重構方式可能會花費開發者更多的時間,但是可以讓程式碼的質量更有保證,同時也使得審查工作更容易進行。

如果確實是沒有辦法進行漸進式重構(這很可能反映了原始程式碼編寫及組織的質量),一個解決方案是在重構過程中使用結對程式設計來代替程式碼審查。

解決爭端

毫無疑問,你的團隊是由很多聰明過人的專家組成的,在對於一個特定的編碼問題意見相左的時候,通常總是能夠達成一致意見。作為一名開發者,我們要保持一個開放的心態,在審查者更傾向於另外一種解決方案時做好讓步的準備。摒棄程式碼為個人所有的心態,並且不要認為審查意見是針對個人的。僅僅有人認為你應該把一些重複的程式碼提取成可複用的函式,並不意味著你本人缺少魅力,智商不高或者不夠英俊。

作為審查者,要做到言行得體。在提出修改意見之前,思考一下你的提議是否真的更好,還是僅僅出於個人品味的不同。如果你慎重地提建議,只專注於程式碼明顯需要改進的地方,那麼,你的建議會被更多得采納。不要說“我家的寵物鼠都可以寫出比這個效率更高的排序演算法”,要用“可以考慮一下......”,或者“有人建議......”來代替。

如果確實雙方都不讓步,那麼邀請一位你們雙方都尊重的開發者來看一看,聽一聽他們的建議。

相關文章