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