Jim Bird是一位經(jīng)驗豐富的軟件開(kāi)發(fā)經(jīng)理、項目經(jīng)理與CTO,專(zhuān)注于軟件開(kāi)發(fā)與維護、軟件質(zhì)量與安全等領(lǐng)域中疑難問(wèn)題的解決。在過(guò)去的15年間,Jim曾管理過(guò)團隊建設并主導過(guò)高性能的財務(wù)系統的建設。他的主要興趣在于如何提升小團隊的效率以構建真正的軟件:高質(zhì)量、安全、可靠、高性能及適應性強。近日,Jim撰寫(xiě)了一篇博文,談到了代碼審查的價(jià)值,如何進(jìn)行代碼審查,代碼審查的過(guò)程以及在代碼審查中需要注意的問(wèn)題,希望能為大家平日的代碼審查帶來(lái)一些啟示。
開(kāi)始代碼審查
從一開(kāi)始,開(kāi)發(fā)者就會(huì )互相幫助,如果測試中遇到了問(wèn)題或是有新人加入到了團隊,領(lǐng)導或是資深開(kāi)發(fā)者就會(huì )審查他們的代碼。除此之外,我們還聘請了外部專(zhuān)家進(jìn)行安全代碼審查。
系統發(fā)布后,我們決定更加主動(dòng)一些,開(kāi)始了基于風(fēng)險的審查:項目中有人會(huì )編寫(xiě)一些風(fēng)險較高的代碼(比如說(shuō)框架與安全代碼、APIs、核心業(yè)務(wù)邏輯或是之前曾經(jīng)出現過(guò)問(wèn)題的地方),我們會(huì )審查他們的代碼。在這個(gè)過(guò)程中,代碼審查體現出了它的價(jià)值,我們收獲頗豐。即便如此,我們還是更進(jìn)一步,讓代碼審查成為一個(gè)標準的實(shí)踐。
這并不是一夜之間就形成的。讓團隊相信代碼審查的價(jià)值并不是什么難事,他們已經(jīng)通過(guò)基于風(fēng)險的審查獲得了收益。不過(guò)要想改變人們的工作方式就不是那么簡(jiǎn)單的事情了,還要確保他們有足夠的時(shí)間進(jìn)行代碼審查,理解并對反饋作出響應。此外,設計一個(gè)高效的代碼審查流程也是需要花時(shí)間的。
一開(kāi)始,我們讓開(kāi)發(fā)者選擇好搭檔并安排審查,但結果卻有些混亂。有時(shí),開(kāi)發(fā)者會(huì )尋找那些好說(shuō)話(huà)或是比較忙的人,這樣審查就比較容易通過(guò)了;此外,兩個(gè)開(kāi)發(fā)者還有可能事先商量好,因此審查過(guò)程就會(huì )很快結束。由于人們并不知道要花費多少時(shí)間才能完成代碼審查,因此審查經(jīng)常會(huì )拖得很久,常常在代碼已經(jīng)完成測試甚至是發(fā)布后才完成。
由于大多數人并沒(méi)有太多的代碼審查經(jīng)驗,因此他們并不確定在審查時(shí)應該看什么,如何給出有意義的反饋等信息。開(kāi)發(fā)者常常會(huì )被負面的批評搞得很沮喪,有時(shí)甚至會(huì )心煩意亂。
最后,我們決定由領(lǐng)導來(lái)完成大部分審查工作。雖然這會(huì )增加領(lǐng)導的工作量,也意味著(zhù)他們沒(méi)有太多時(shí)間編寫(xiě)代碼了,不過(guò)這么做卻是很有效果的。通常情況下,主開(kāi)發(fā)者會(huì )對需求有著(zhù)更好的理解,對代碼的行為有著(zhù)清晰的認識,這也意味著(zhù)他們更有可能發(fā)現代碼中的錯誤。由于是同一個(gè)人完成了大部分的代碼審查,因此被審查的開(kāi)發(fā)者會(huì )收到一致的反饋信息。
我們如何進(jìn)行代碼審查
在過(guò)去的幾年間,我們進(jìn)行代碼審查的方式幾乎沒(méi)有發(fā)生過(guò)什么大的變化。
無(wú)論是誰(shuí)編寫(xiě)的,無(wú)論代碼的功能是什么,重要的代碼變更是一定要審查的。我們并沒(méi)有一個(gè)正式的審查會(huì )議,也沒(méi)發(fā)現使用諸如Code Collaborator、Crucible等工具有什么必要性,不過(guò)現在看起來(lái)使用這些工具來(lái)管理和追蹤審查有助于團隊更好的起步。
有時(shí),審查是面對面完成的,不過(guò)大多數時(shí)候都是離線(xiàn)進(jìn)行的。審查者與開(kāi)發(fā)者會(huì )交換信息,也許通過(guò)郵件發(fā)送文件,因為我們覺(jué)得這種方式更加便捷,也更加方便每一個(gè)人安排自己的時(shí)間。
隨著(zhù)時(shí)間的流逝,審查中的變化之處是審查者該看什么,以及看到的結果。
審查正確性
很多時(shí)候,程序員們會(huì )花費很多時(shí)間爭論在代碼審查過(guò)程中應該看什么,但實(shí)際上他們卻沒(méi)有花太多時(shí)間完成真正的代碼審查。
我們開(kāi)始代碼審查的目的是改進(jìn)代碼質(zhì)量,尋找測試中沒(méi)有發(fā)現的問(wèn)題。這么做并不是教授經(jīng)驗不足的開(kāi)發(fā)者如何編寫(xiě)更好的代碼,或是如何在團隊內分享知識。這些都應該是代碼審查所帶來(lái)的間接好處,不過(guò)審查的最終目的應該是確保代碼能夠正常工作。
相對于使用長(cháng)長(cháng)的檢查列表,審查者在看代碼時(shí)首先會(huì )問(wèn)幾個(gè)問(wèn)題:
代碼的行為是否與預期一致,其邏輯是否是正確無(wú)誤的?
被審查的代碼是否與其他代碼擁有類(lèi)似的結構和功能?
審查可理解性
隨著(zhù)時(shí)間的流逝,在代碼審查中尋找和得到的東西會(huì )發(fā)生變化。這是因為代碼本身會(huì )發(fā)生變化,完成這項工作的人——開(kāi)發(fā)者與審查者也可能發(fā)生變化。
開(kāi)發(fā)者需要在其IDE中裝上代碼檢查工具,同時(shí)我們也要使用一些優(yōu)秀的靜態(tài)分析工具來(lái)自動(dòng)檢查常見(jiàn)的編碼Bug和不好的編碼實(shí)踐。這意味著(zhù)審查者的時(shí)間可以花在更加重要的設計錯誤上,比如說(shuō)并發(fā)Bug、競態(tài)條件和潛在的死鎖等,因為這些問(wèn)題是工具所無(wú)法檢測到的。
理解,而不是批評
審查的目的是理解代碼,確保代碼能夠正常工作,而不是批評任何人。
在代碼審查過(guò)程中,請不要摻入諸如“我認為好的代碼是什么,你認為好的代碼是什么”這樣的論戰之中。代碼審查應該重點(diǎn)關(guān)注“我需要完全理解這部分代碼才能確保它能夠正常工作,如果由我來(lái)修復代碼中的問(wèn)題,我是不會(huì )這么寫(xiě)的,因此希望你也不要這么來(lái)寫(xiě)”。
隨著(zhù)時(shí)間的流逝,有些東西會(huì )變好,有些則不然
隨著(zhù)時(shí)間的流逝,在查看代碼時(shí)你會(huì )發(fā)現代碼在不斷變化。Michael Feathers發(fā)現大部分代碼在寫(xiě)完后很少或是從來(lái)都不會(huì )變化;大部分變更都是發(fā)生在代碼基中的很小一部分代碼上的。
由于審查者會(huì )看到相同的代碼在不斷發(fā)生著(zhù)變化,因此這也會(huì )改變他們的審查方式。
避免收益遞減
類(lèi)似于軟件開(kāi)發(fā)中的其他實(shí)踐一樣,你最終會(huì )發(fā)現代碼審查有著(zhù)收益遞減的效應,特別是一直在做相同的事情的時(shí)候,以相同的方式查看相同的問(wèn)題。最終會(huì )有這樣一個(gè)階段,那就是代碼審查的價(jià)值幾乎降到了0。不過(guò)這種情況并沒(méi)有發(fā)生在我們身上,我們依然從代碼審查過(guò)程中獲得了很多收益。
即便改進(jìn)了工具,增強了測試能力,我們依然進(jìn)行代碼審查,因為Bug檢查工具、審查與測試會(huì )發(fā)現不同類(lèi)型的問(wèn)題。我們還發(fā)現代碼審查會(huì )讓測試更加高效,這是因為如果之前曾經(jīng)發(fā)現過(guò)問(wèn)題,那么開(kāi)發(fā)者與測試者之間就不會(huì )出現過(guò)多的往復情況。
原文轉自:http://kb.cnblogs.com/page/199658/