數(shù)百萬年前,猿猴從樹上下來,進化出了對生拇指,最終進化為人類。
我們在強制代碼審查上面看到了相似的曙光:它就像是在軟件開發(fā)大草原上將人和野獸區(qū)分開來的東西。
盡管如此,我還是不時地聽到團隊中有這樣的評論:
“在這個項目上進行代碼審查是浪費時間?!?/p>
“我沒有時間來做代碼審查?!?/p>
“發(fā)布被延期了,因為我那卑鄙的同事還沒有審查我的代碼?!?/p>
“你能相信嗎我的同事竟然想讓我修改我的部分代碼?請解釋給他們聽,如果我這極好的原始代碼以任何方式被修改了,那么宇宙的微妙平衡就會被打破?!?/p>
我們?yōu)槭裁匆龃a審查?
首先,讓我們牢記為什么要做代碼審查。所有專業(yè)的軟件開發(fā)人員最重要的目標之一就是不斷地提高工作的質(zhì)量。即使你的團隊里滿滿都是優(yōu)秀的程序員,如果你們不能作為一個團隊而協(xié)作,那么你們在一名能干的自由職業(yè)者面前也沒有優(yōu)勢可言。代碼審查是達到這一目標最重要的途徑之一。特別是,代碼審查:
提供了另一雙眼睛去發(fā)現(xiàn)缺陷和更好的做事方法。
保證至少有另外一個人熟悉你的代碼。
通過讓新員工接觸更富有經(jīng)驗的程序員的代碼來幫助培養(yǎng)他們。
同時讓審查者和被審查者接觸他人的好想法和實踐經(jīng)驗來促進知識共享。
激勵開發(fā)人員在工作中更加認真,因為他們知道他們的代碼會被另一個同事審查。
進行全面的代碼審查
然而,除非在審查中投入恰當(dāng)?shù)臅r間和精力,否則無法達到這些目標。只是滾動瀏覽一遍代碼,確??s進是正確的,所有的變量都使用了小駝峰式命名法,這并不能構(gòu)成一次全面的代碼審查。將結(jié)對編程看作是代碼審查花銷的基準是有益的,結(jié)對編程是一種頗為流行的做法,它會將所有的開發(fā)時間增加100%的額外開銷。 你可以花費大量的時間在代碼審查上卻依然比結(jié)對編程花費更少的總工程師時間。
我認為原始開發(fā)時間的25%左右的時間應(yīng)該被用于代碼審查。比如,如果一名開發(fā)人員花費兩天實現(xiàn)一個功能點,審查者應(yīng)該花費大約四小時對其做代碼審查。
當(dāng)然,只要正確的完成了審查,具體花費多長時間在審查上并不是最重要的。特別是,你必須理解你正在審查的代碼。這并不僅僅表示你要了解代碼所用編程語言的語法規(guī)則,它意味著你必須明白這段代碼是如何融入應(yīng)用程序環(huán)境的,不論是組件還是以庫文件的形式。如果你沒有領(lǐng)會每一行代碼的意思,那么你的審查將不會特別有用。這就是為什么好的代碼審查無法很快地完成:需要花費時間來查看各種可以觸發(fā)特定函數(shù)的代碼分支,確保第三方API被正確地使用(包括任何邊界情況),等等。
除了查找審查代碼中的缺陷和其他問題之外,你應(yīng)該確保:
包含了所有必要的測試。
寫了合適的設(shè)計文檔。
即使是擅于寫測試和文檔的開發(fā)者也不總能記得修改了代碼之后更新它們。在適當(dāng)?shù)臅r候,來自代碼審查者的小小提醒對于保證測試和文檔不會隨時間而過時是至關(guān)重要的。
避免過度地審查代碼
如果你的團隊進行強制的代碼審查,風(fēng)險是代碼審查將會積壓到無法管理的地步。如果你兩周不做任何代碼審查,將很容易就需要花費數(shù)天來趕上進度,這意味著當(dāng)你最終決定處理它們的時候,你自己的開發(fā)工作將會受到巨大的意想不到的影響。這種情況下保持代碼審查的質(zhì)量會尤為困難,因為恰當(dāng)?shù)拇a審查需要強烈持續(xù)的腦力勞動,這很難持續(xù)不斷地進行數(shù)天。
基于這個原因,開發(fā)者們應(yīng)該盡量每天完成積壓的審查工作。一種方法是將審查作為上午第一件事來做,在開始自己的開發(fā)工作之前完成所有審查,你可以避免審查落入失控的情況。一些人可能傾向于在午休前后或者一天結(jié)束時做審查。無論何時做審查,通過將代碼審查視為你日常工作的一部分而不是一件使人分心的事,你避免了:
沒有時間去處理積壓的審查工作。
因為你的審查還沒有完成而耽擱產(chǎn)品發(fā)布。
發(fā)布已經(jīng)過時的審查結(jié)果,因為在此期間代碼已經(jīng)大大修改了。
進行粗劣的審查,因為你不得不在最后時刻倉促完成。
編寫可審查的代碼
審查者并不總是造成失控的審查積壓工作的唯一原因。如果我的同事花費一周在一個大項目里亂糟糟地添加代碼,那么他們發(fā)布的代碼補丁將會非常難審查。在一個審查步驟上將會有太多工作要做,理解代碼的目的和隱含的架構(gòu)將會非常困難。
因此,將工作劃分為可管理的小單元非常重要。我們使用Scrum方法論,所以對我們來說,用例就是合適的單元。盡量用用例來組織我們的工作,提交只屬于我們當(dāng)前在處理的用例的審查,這樣我們寫的代碼將會非常容易審查。你的團隊可以使用其他的方法論,但原理是一樣的。
編寫可審查的代碼還有一些其他的先決條件。如果要做出復(fù)雜的代碼架構(gòu)決策,提前和審查者見面討論一下將十分有用,這將使審查者能夠比較容易理解你的代碼,因為他們將明白你要達到什么目標,以及你計劃如何達到它。這同時能夠幫助避免當(dāng)審查者建議了另一種更好的方法時你不得不重寫大塊代碼的情況。
項目的架構(gòu)應(yīng)該在你的設(shè)計文檔中進行詳細的描述,無論如何這都是很重要的,因為它可以使新的項目成員熟悉了解新項目的詳細情況并且理解已有的代碼庫。另外的好處是能夠幫助審查者進行恰當(dāng)?shù)拇a審查。單元測試同樣有助于向?qū)彶檎哒f明組件應(yīng)該如何被使用。
如果你的補丁里包含了第三方代碼,將它們單獨提交。如果你的代碼中間突然跳出9000行的jQuery代碼,這將會非常難進行恰當(dāng)?shù)拇a審查。
編寫可審查代碼的其中一個非常重要的步驟是將自己的代碼審查進行注釋,即你自己審查代碼,并在任何你認為能夠幫助審查者理解這里是怎么回事的地方添加注釋。我發(fā)現(xiàn)給代碼添加注釋只花費相對較少的時間(通常只幾分鐘),卻在代碼審查的速度和質(zhì)量上產(chǎn)生巨大差別。當(dāng)然,代碼的注釋有許多同樣的好處,并且應(yīng)該被恰當(dāng)使用,但通常評審注釋更有意義。另外,研究表明當(dāng)重讀和審查自己的代碼時,開發(fā)者能夠發(fā)現(xiàn)許多缺陷。
大型代碼重構(gòu)
有時,有必要用一種會影響許多組件的方式來重構(gòu)一個代碼庫。就一個大的應(yīng)用程序來說,這會花費數(shù)天(或者更久),產(chǎn)生一個很大的補丁。在這種情況下進行標準的代碼審查可能是不現(xiàn)實的。
最好的解決方案是增量式地重構(gòu)代碼。在合理范圍內(nèi)找出部分的代碼改動,它能夠使代碼庫正常工作,并且是朝向你想要達到的目標方向的改變。一旦這個修改完成了并且審查也通過了,進行下一步增量式的修改,如此等等直到全部重構(gòu)完成。這種方法也許并不總是可行的,但通過思考和計劃,通常能夠避免在重構(gòu)時產(chǎn)生巨大的整體一塊的補丁。用這種方式,對于開發(fā)者來說可能會花費更多的時間來進行重構(gòu),但也提高了代碼質(zhì)量,也使審查變得容易多了。
如果實在無法進行增量式代碼重構(gòu)(可能說明原始代碼的編寫和組織有多好),一種解決方法大概是在進行重構(gòu)時采取結(jié)對編程的方式來替代代碼審查。
解決分歧
你的團隊無疑是由聰明的專業(yè)人士組成的,并在幾乎所有情況下,當(dāng)在某個編碼問題上發(fā)生意見分歧時應(yīng)該可以達成一致的意見。當(dāng)你的審查者傾向于另一種不同的方法時,作為一名開發(fā)者,要保持開放的態(tài)度,并準備作出妥協(xié)。不要對你的代碼有種專有的態(tài)度,也不要認為審查意見是針對個人的。僅僅因為有人覺得你應(yīng)該將一些重復(fù)的代碼重構(gòu)為可重用的函數(shù)并不意味著你就不是一個有吸引力的、風(fēng)趣的、聰明的、迷人的人了。
作為一個審查者,要機智些。在建議修改之前,考慮好你的建議是否真的是更好的,還是只是編碼習(xí)慣不同的問題。如果你專注于解決那些原始代碼明顯需要改善的部分,你會做得更好。要像這樣對開發(fā)者說話:“這可能是值得考慮的……”或“有些人建議……”,而不是“我的寵物倉鼠都可以寫一個比這個更高效的排序算法”。
如果你們實在無法達成一致意見,再去找一個你們兩個都尊重的開發(fā)者來看一看,給出他的意見。
更多信息請查看IT技術(shù)專欄