同行程式碼審查的實戰經驗

pinkS發表於2015-03-27

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

我們在強制程式碼審查上面看到了相似的曙光:它就像是在軟體開發大草原上將人和野獸區分開來的東西。

儘管如此,我還是不時地聽到團隊中有這樣的評論:

  • “在這個專案上進行程式碼審查是浪費時間。”
  • “我沒有時間來做程式碼審查。”
  • “釋出被延期了,因為我那卑鄙的同事還沒有審查我的程式碼。”
  • “你能相信嗎我的同事竟然想讓我修改我的部分程式碼?請解釋給他們聽,如果我這極好的原始程式碼以任何方式被修改了,那麼宇宙的微妙平衡就會被打破。”

我們為什麼要做程式碼審查?

首先,讓我們牢記為什麼要做程式碼審查。所有專業的軟體開發人員最重要的目標之一就是不斷地提高工作的質量。即使你的團隊裡滿滿都是優秀的程式設計師,如果你們不能作為一個團隊而協作,那麼你們在一名能幹的自由職業者面前也沒有優勢可言。程式碼審查是達到這一目標最重要的途徑之一。特別是,程式碼審查:

  • 提供了另一雙眼睛去發現缺陷和更好的做事方法。
  • 保證至少有另外一個人熟悉你的程式碼。
  • 通過讓新員工接觸更富有經驗的程式設計師的程式碼來幫助培養他們。
  • 同時讓審查者和被審查者接觸他人的好想法和實踐經驗來促進知識共享。
  • 激勵開發人員在工作中更加認真,因為他們知道他們的程式碼會被另一個同事審查。

進行全面的程式碼審查

然而,除非在審查中投入恰當的時間和精力,否則無法達到這些目標。只是滾動瀏覽一遍程式碼,確保縮排是正確的,所有的變數都使用了小駝峰式命名法,這並不能構成一次全面的程式碼審查。將結對程式設計看作是程式碼審查花銷的基準是有益的,結對程式設計是一種頗為流行的做法,它會將所有的開發時間增加100%的額外開銷。 你可以花費大量的時間在程式碼審查上卻依然比結對程式設計花費更少的總工程師時間。

我認為原始開發時間的25%左右的時間應該被用於程式碼審查。比如,如果一名開發人員花費兩天實現一個功能點,審查者應該花費大約四小時對其做程式碼審查。

當然,只要正確的完成了審查,具體花費多長時間在審查上並不是最重要的。特別是,你必須理解你正在審查的程式碼。這並不僅僅表示你要了解程式碼所用程式語言的語法規則,它意味著你必須明白這段程式碼是如何融入應用程式環境的,不論是元件還是以庫檔案的形式。如果你沒有領會每一行程式碼的意思,那麼你的審查將不會特別有用。這就是為什麼好的程式碼審查無法很快地完成:需要花費時間來檢視各種可以觸發特定函式的程式碼分支,確保第三方API被正確地使用(包括任何邊界情況),等等。

除了查詢審查程式碼中的缺陷和其他問題之外,你應該確保:

  • 包含了所有必要的測試。
  • 寫了合適的設計文件。

即使是擅於寫測試和文件的開發者也不總能記得修改了程式碼之後更新它們。在適當的時候,來自程式碼審查者的小小提醒對於保證測試和文件不會隨時間而過時是至關重要的。

避免過度地審查程式碼

如果你的團隊進行強制的程式碼審查,風險是程式碼審查將會積壓到無法管理的地步。如果你兩週不做任何程式碼審查,將很容易就需要花費數天來趕上進度,這意味著當你最終決定處理它們的時候,你自己的開發工作將會受到巨大的意想不到的影響。這種情況下保持程式碼審查的質量會尤為困難,因為恰當的程式碼審查需要強烈持續的腦力勞動,這很難持續不斷地進行數天。

基於這個原因,開發者們應該儘量每天完成積壓的審查工作。一種方法是將審查作為上午第一件事來做,在開始自己的開發工作之前完成所有審查,你可以避免審查落入失控的情況。一些人可能傾向於在午休前後或者一天結束時做審查。無論何時做審查,通過將程式碼審查視為你日常工作的一部分而不是一件使人分心的事,你避免了:

  • 沒有時間去處理積壓的審查工作。
  • 因為你的審查還沒有完成而耽擱產品釋出。
  • 釋出已經過時的審查結果,因為在此期間程式碼已經大大修改了。
  • 進行粗劣的審查,因為你不得不在最後時刻倉促完成。

編寫可審查的程式碼

審查者並不總是造成失控的審查積壓工作的唯一原因。如果我的同事花費一週在一個大專案裡亂糟糟地新增程式碼,那麼他們釋出的程式碼補丁將會非常難審查。在一個審查步驟上將會有太多工作要做,理解程式碼的目的和隱含的架構將會非常困難。

因此,將工作劃分為可管理的小單元非常重要。我們使用Scrum方法論,所以對我們來說,用例就是合適的單元。儘量用用例來組織我們的工作,提交只屬於我們當前在處理的用例的審查,這樣我們寫的程式碼將會非常容易審查。你的團隊可以使用其他的方法論,但原理是一樣的。

編寫可審查的程式碼還有一些其他的先決條件。如果要做出複雜的程式碼架構決策,提前和審查者見面討論一下將十分有用,這將使審查者能夠比較容易理解你的程式碼,因為他們將明白你要達到什麼目標,以及你計劃如何達到它。這同時能夠幫助避免當審查者建議了另一種更好的方法時你不得不重寫大塊程式碼的情況。

專案的架構應該在你的設計文件中進行詳細的描述,無論如何這都是很重要的,因為它可以使新的專案成員熟悉瞭解新專案的詳細情況並且理解已有的程式碼庫。另外的好處是能夠幫助審查者進行恰當的程式碼審查。單元測試同樣有助於向審查者說明元件應該如何被使用。

如果你的補丁裡包含了第三方程式碼,將它們單獨提交。如果你的程式碼中間突然跳出9000行的jQuery程式碼,這將會非常難進行恰當的程式碼審查。

編寫可審查程式碼的其中一個非常重要的步驟是將自己的程式碼審查進行註釋,即你自己審查程式碼,並在任何你認為能夠幫助審查者理解這裡是怎麼回事的地方新增註釋。我發現給程式碼新增註釋只花費相對較少的時間(通常只幾分鐘),卻在程式碼審查的速度和質量上產生巨大差別。當然,程式碼的註釋有許多同樣的好處,並且應該被恰當使用,但通常評審註釋更有意義。另外,研究表明當重讀和審查自己的程式碼時,開發者能夠發現許多缺陷。

大型程式碼重構

有時,有必要用一種會影響許多元件的方式來重構一個程式碼庫。就一個大的應用程式來說,這會花費數天(或者更久),產生一個很大的補丁。在這種情況下進行標準的程式碼審查可能是不現實的。

最好的解決方案是增量式地重構程式碼。在合理範圍內找出部分的程式碼改動,它能夠使程式碼庫正常工作,並且是朝向你想要達到的目標方向的改變。一旦這個修改完成了並且審查也通過了,進行下一步增量式的修改,如此等等直到全部重構完成。這種方法也許並不總是可行的,但通過思考和計劃,通常能夠避免在重構時產生巨大的整體一塊的補丁。用這種方式,對於開發者來說可能會花費更多的時間來進行重構,但也提高了程式碼質量,也使審查變得容易多了。

如果實在無法進行增量式程式碼重構(可能說明原始程式碼的編寫和組織有多好),一種解決方法大概是在進行重構時採取結對程式設計的方式來替代程式碼審查。

解決分歧

你的團隊無疑是由聰明的專業人士組成的,並在幾乎所有情況下,當在某個編碼問題上發生意見分歧時應該可以達成一致的意見。當你的審查者傾向於另一種不同的方法時,作為一名開發者,要保持開放的態度,並準備作出妥協。不要對你的程式碼有種專有的態度,也不要認為審查意見是針對個人的。僅僅因為有人覺得你應該將一些重複的程式碼重構為可重用的函式並不意味著你就不是一個有吸引力的、風趣的、聰明的、迷人的人了。

作為一個審查者,要機智些。在建議修改之前,考慮好你的建議是否真的是更好的,還是隻是編碼習慣不同的問題。如果你專注於解決那些原始程式碼明顯需要改善的部分,你會做得更好。要像這樣對開發者說話:“這可能是值得考慮的……”或“有些人建議……”,而不是“我的寵物倉鼠都可以寫一個比這個更高效的排序演算法”。

如果你們實在無法達成一致意見,再去找一個你們兩個都尊重的開發者來看一看,給出他的意見。

相關文章