谷歌開源內部代碼評審規範

谷歌成立於 1998 年,以搜索起家,到目前爲止已經發展了 21 年。在過去的 21 年中,谷歌不斷創新,開發了七款產品,擁有超過 10 億級活躍用戶,谷歌的工程師文化一直被認爲是優秀且特別的。近日,谷歌開源了其內部一直在使用的代碼評審規範,InfoQ對其進行了翻譯和整理,分享給廣大開發者,看看谷歌工程師是如何評審代碼的。

代碼評審標準

代碼評審的主要目的是確保代碼庫的整體質量隨時間推移逐步得到提升,所有代碼評審工具和過程都是爲了實現這一目標而設計的。

爲了實現這個目標,必須做出一系列權衡。首先,開發人員的開發任務必須要有所進展。如果他們不提交改進的代碼,代碼庫質量就得不到改善。此外,如果評審人員過於嚴格,開發人員就沒有動力進行持續改進。

評審人員的職責是確保每個CL(變更列表)的質量,保證代碼庫整體質量不會隨着時間的推移而下降。這是一項艱鉅的任務,因爲代碼庫整體質量常常會隨着每次提交代碼質量的小幅下降而退化,特別是有時候開發團隊時間很緊,並認爲必須走捷徑才能完成交付任務。

評審人員要對他們評審的代碼負起責任,確保代碼庫保持一致性和可維護性。

以下是可在代碼評審中使用的準則:

一般來說,如果CL達到可以提升系統整體代碼質量的程度,就可以讓它們通過了,即使它們可能還不完美。

這是所有代碼評審準則的最高原則。

當然,也有例外的時候。例如,如果CL中包含了系統不需要的功能,那麼即使代碼寫得很好,評審人員也可以拒絕讓它們通過。

這個世界上沒有“完美”的代碼,只有更好的代碼。評審人員不應該要求開發人員對CL中的每一個微小部分都進行細緻入微的打磨,而應該在滿足需求和變更重要性之間做出權衡。評審人員不應該追求完美,而應該追求持續改進。如果一個CL能夠從整體上提高系統的可維護性、可讀性和可理解性,那它就不應該僅僅因爲它不夠“完美”而被延遲幾天甚至幾周。

評審人員應該提供建議,告訴開發人員哪些方面可以做得更好。但如果這些建議不是很重要,可以在前面加上像“Nit:”這樣的前綴,讓開發人員知道這只是一個改進建議,他們也可以選擇忽略。

指導

代碼評審的一個作用是向開發人員傳授知識,比如關於一門語言、一個框架或一般軟件設計原則的知識。分享知識是提升系統代碼質量的一個組成部分。但要注意,如果你的建議純粹是帶有教育性質的,並且對於滿足本文所描述的標準來說並不是那麼重要,那麼請在前面加上“Nit:”,或者以其他方式告訴開發人員,他們並不一定要在CL中解決這些問題。

原則

  • 客觀的技術和數據比個人意見和偏好更重要。

  • 在代碼風格方面,可以參考谷歌風格指南(http://google.github.io/styleguide)。任何沒有在這個風格指南中出現的東西(比如空格等)都屬於個人偏好。代碼風格應該與原有代碼保持一致,如果之前沒有規定代碼風格,可以使用代碼提交者的代碼風格。

  • 軟件設計從來就不只風格問題,也不只是個人偏好問題。它們建立在一些基本原則之上,所以我們應該基於這些原則做出權衡,而不只是基於個人偏好。有時候,一個問題有多種解決方案,如果開發人員能夠證明(通過數據或基於可靠的工程原理)幾種解決方案是同樣有效的,那麼評審人員應該接受開發人員的選擇,否則就應該基於軟件設計標準原則做出決定。

  • 如果沒有其他適用的原則,評審人員可以要求開發人員與當前代碼庫保持一致,只要不破壞系統的整體代碼質量。

解決衝突

在代碼評審過程中出現衝突時,開發人員和評審人員首先要嘗試根據本文、CL作者指南(https://google.github.io/eng-practices/review/developer/)和評審人員指南(https://google.github.io/eng-practices/review/reviewer/)達成一致意見。

如果很難達成一致意見,評審人員和開發人員可以進行面對面會議或者視頻會議,而不是隻是試圖通過代碼評審評論板來解決衝突。

如果還不能解決問題,那麼就要考慮把問題升級,進行更廣泛的團隊討論。讓團隊負責人蔘與進來,請求代碼維護人員作出決定,或請求工程經理提供幫助。不要因爲開發人員和評審人員無法達成一致意見就讓CL一直掛在那裏。

代碼評審要注意哪些事情?

設計

代碼評審中最重要的部分是CL的總體設計。CL中不同代碼段之間的交互是有意義的嗎?這個變更應該屬於代碼庫,還是屬於某個包?它與系統的其他部分可以良好地集成嗎?現在是引入這個變更的好時機嗎?

功能

這個CL是否達到了開發人員的目的?開發人員的意圖對代碼用戶來說有好處嗎?代碼“用戶”可以是指最終用戶(他們受代碼變更的影響)和開發人員(將來要“使用”這些代碼)。

大多數情況下,我們希望開發人員先測試好CL,確保它們能夠正確運行。但作爲評審人員,你仍然要考慮一些邊緣情況,比如查找併發問題,嘗試像用戶一樣思考問題,並找出只是通過閱讀代碼無法看到的錯誤。

如果願意,你也可以驗證一下CL。如果一個CL會影響用戶,比如做出了UI變更,那麼這是驗證CL的好時機。如果只是看代碼,很難理解一些變更將如何影響用戶。對於這樣的更改,如果不方便自己運行,可以讓開發人員提供功能演示。

另一個重要的考慮點是CL中是否存在可能導致死鎖或競態條件的併發問題。只是簡單地運行代碼很難發現這類問題,通常需要有人(開發人員和評審人員)仔細思考這些問題,確保不會把它們引入到系統中。

複雜性

CL比實際需要的更復雜嗎?從每一層面檢查CL,細到每一行代碼,它們是不是太複雜了?函數是否過於複雜?類複雜嗎?“太複雜”通常意味着“閱讀代碼的人難以很快理解它們”,也意味着“開發人員在調用或修改這些代碼時可能會引入bug”。

過度設計是一種特殊的複雜性,開發人員把代碼寫得比實際需要的更通用,或者增加了系統當前不需要的功能。評審人員要警惕過度設計,鼓勵開發人員只解決現在需要解決的問題,而不是將來可能需要解決的問題。未來的問題應該在它們出現之後再去解決,因爲到了那個時候我們可以看到它們的實際狀況和需求。

測試

要求開發人員進行單元測試、集成測試或端到端測試。一般來說,CL中應該包含測試,除非這個CL只是爲了處理緊急情況。

確保CL中的測試是正確、合理和有用的。因爲測試本身無法測試自己,而且我們很少會爲測試編寫測試,所以必須確保測試是有效的。

如果代碼出了問題,測試會失敗嗎?如果代碼發生改動,它們會誤報嗎?每一個測試都有斷言嗎?是否按照不同的測試方法對測試進行分類?

請記住,測試代碼也是需要維護的。

命名

開發人員是否使用了良好的命名方式?好的命名要能夠充分表達一個項(變量、類名等)是什麼或者用來做什麼,但又不至於讓人難以閱讀。

註釋

開發人員有沒有用自然語言寫出清晰的註釋?他們所寫的註釋都是必需的嗎?通常,註釋應該用於解釋代碼的用處,而不是解釋它們在幹什麼。如果代碼不夠清晰,無法自解釋,那就應該簡化代碼。當然也有一些例外(例如,正則表達式和複雜的算法,如果能夠解釋它們在做什麼,會讓閱讀代碼的人受益匪淺),但大多數註釋都應該指出代碼中不可能包含的信息,比如這些代碼背後的緣由。

CL附帶的其他註解也很重要,比如告知一個可以移除的待辦事項,或者一個不要做出代碼變更的建議,等等。

注意,註釋不同於類、模塊或函數文檔。文檔的目的是爲了說明代碼的用途、用法和行爲。

代碼風格

谷歌爲主要編程語言和大多數次要編程語言提供了代碼風格指南(http://google.github.io/styleguide/),所以要確保CL遵循了適當的指南。

如果你想對指南中沒有提及的風格做出改進,可以在註釋前面加上“Nit:”,讓開發人員知道這是一個你認爲可以改進的地方,但不是強制性的。但請不要只是基於個人偏好來阻止CL的提交。

開發人員不應該將風格變更與其他變更放在一起,這樣很難看出CL發生了哪些變化,導致合併和回滾變得更加複雜。如果開發人員想要重新格式化整個文件,讓他們將重新格式化後的文件作爲單獨的CL,並將功能變更作爲另一個CL。

文檔

如果CL導致用戶構建、測試、交互或發佈代碼的方式發生了變化,請確保相關的文檔也得到了更新,包括README、g3doc頁和其他生成的參考文檔。如果CL有移除或棄用代碼,請考慮一下是否也應該刪除相關的文檔。如果文檔缺失,要向開發人員索要。

查看每一行代碼

查看每一行代碼。有些東西可以看一看,比如數據文件、生成的代碼或大型數據結構,但不要只是粗略地掃一下類、函數或代碼塊,並假定它們都能正常運行。顯然,有些代碼需要仔細檢查,至於是哪些代碼完全取決於你,但你至少應該要理解這些代碼都在做些什麼。

如果代碼很複雜或者你難以快速看懂它們,導致評審速度變慢,你要讓開發人員知道,並在進行進一步評審之前讓他們做一些澄清。如果你看不懂這些代碼,其他開發人員很可能也看不懂。因此,要求開發人員澄清代碼其實也是在幫助未來的開發人員更好地理解代碼。

如果你理解代碼,但又覺得沒有資格做代碼評審,可以確保有資格的CL評審人員在代碼評審時考慮到了安全性、併發性、可訪問性、國際化等問題。

上下文

代碼評審工具通常只顯示被修改的代碼,但有時候你需要查看整個文件,確保代碼變更是有意義的。例如,你可能只看到新添加了四行代碼,但如果你看一下整個文件,會發現這四行代碼位於一個50多行的方法中,這個時候需要將這個方法拆分爲更小的方法。

你需要基於整個系統來考量CL。這個CL是提升了系統的代碼質量,還是讓整個系統變得更復雜、更不可測?不要接受導致系統代碼質量退化的CL。大多數系統都是因爲累積了很多小的變更而變複雜的,所以要儘量避免小的變更帶來的複雜性。

好的一面

如果你在CL中看到一些不錯的東西,要讓開發人員知道,特別是當他們以一種很好的方式解決了問題。代碼評審通常只關注錯誤的東西,但其實也應該鼓勵和讚賞好的代碼實踐。有時候,讓開發人員知道他們做對了事情比讓他們知道做錯了事情更有價值。

總結

在進行代碼評審時,你要確保:

  • 良好的代碼設計。
  • 功能對代碼用戶來說是有用的。
  • UI變更應該是合理的。
  • 並行編程是安全的。
  • 代碼複雜性不要超過應有的程度。
  • 不需要實現可能會在未來出現的需求。
  • 有適當的單元測試。
  • 精心設計的測試用例。
  • 使用了清晰的命名方式。
  • 清晰而有用的代碼註釋,要解釋“爲什麼”,而不是“什麼”。
  • 恰如其分的代碼文檔化。
  • 代碼要遵循風格指南。

檢查每一行代碼,查看上下文,確保你正在改進代碼質量,併爲表現不錯的開發人員點贊。

檢查CL

在知道了代碼評審要關注哪些東西之後,如何有效地進行跨文件代碼評審呢?

  • 代碼變更有意義嗎?它們有沒有良好的描述?
  • 先看一下代碼變更中最重要的部分,它整體設計得如何?
  • 按照適當的順序檢查CL的其餘部分。

第一步:從整體查看代碼變更

先看一下CL描述,看看這個CL做了些什麼。做出這個變更有意義嗎?如果這個變更是不必要的,請立即做出回覆,並解釋爲什麼不應該發生這個變更。在你拒絕這樣的變更時,可以向開發人員建議他們應該做些什麼。

例如,你可以說:“看起來你在這方面做得不錯,謝謝!不過,我們正打算移除這個系統,所以現在不想對它做任何修改。或許你可以重構一下另外一個類”?

注意,評審人員在拒絕一個CL並提供替代建議時要做得很有禮貌。禮貌是很重要的,因爲作爲開發人員,我們要彼此尊重,即使可能意見不一致。

如果有很多CL是你不希望出現的,就要考慮重新調整開發團隊或外部貢獻者的開發流程,以便在開發新的CL之前進行更多的溝通。提前告訴人們哪些事情不要做,這比等他們做完了這些事情再把它們扔掉或者進行徹底重寫要好得多。

第二步:檢查CL的主要部分

找到CL的主要文件。通常一個CL會有一個包含了主要邏輯變更的文件,也就是CL的主要部分。先看看這些主要部分,有助於瞭解整個上下文,加快代碼評審速度。如果CL太大,以致於你無法確定哪些部分是主要的,可以詢問開發人員,或者讓他們把CL拆分成多個CL。

如果CL的主要部分存在嚴重的設計問題,要立即回覆開發人員,即使你還沒有時間檢查CL的其餘部分。這個時候檢查CL的其餘部分可能是在浪費時間,因爲如果主要部分存在嚴重的設計問題,那麼其他部分就變得無關緊要了。

爲什麼要立即回覆開發人員?原因有二:

  • 開發人員在發出一個CL之後會繼續開始後續的開發工作。如果你正在評審的CL存在嚴重的設計問題,他們也需要重寫後續的CL。所以,最好趕在開發人員在有問題的設計上花費不必要的時間之前告訴他們。

  • 大的設計變更比小的變更需要更長的時間。爲了讓開發人員能夠在截止日期之前提交代碼,同時又能保持代碼庫的質量,要儘早讓他們開始重寫工作。

第三步:按照適當的順序檢查CL的其餘部分

在確認整體CL沒有嚴重的設計問題之後,試着按照某種邏輯順序來檢查其他文件,確保不會錯過任何一個需要檢查的文件。通常,在你檢查完主要文件之後,按照代碼評審工具顯示它們的順序來瀏覽每個文件就可以了。你也可以在檢查主要代碼之前先查看測試代碼,這樣可以對代碼變更有一個大致的概念。

代碼評審的速度

爲什麼代碼評審要快速進行?

在谷歌,我們對開發團隊的整體交付速度(而不是針對個體開發人員寫代碼的速度)進行了優化。個體開發速度也很重要,但其重要性比不上整個團隊的開發速度。

如果代碼評審的速度很慢,就會發生以下這些事情:

  • 團隊的整體開發速度降低了。如果個體開發人員無法快速地對評審做出響應,可能是因爲他們有其他事情要做。但是,如果每個CL都要等待一次又一次的評審,那麼其他成員的新特性和bug修復就會被延遲,可能是幾天、幾周甚至是幾個月。

  • 開發人員開始對代碼評審流程提出抗議。如果評審人員要隔幾天纔回復一次,但每次都要求對CL進行重大修改,開發人員可能會覺得很沮喪。通常,他們會抱怨評審人員太過嚴苛。如果評審人員能夠快速提供反饋,抱怨就會消失,即使他們要求做出的修改是一樣的。代碼評審過程的大多數抱怨實際上可以通過加快評審速度來解決。

  • 代碼質量受影響。如果評審速度很慢,開發人員的壓力也會隨之增加,因爲他們不能提交不甚完美的CL。緩慢的評審流程還會阻礙代碼清理、重構和對現有CL做出進一步改進。

代碼評審應該要多快?

如果你不是在集中精力完成手頭的任務,那就應該在第一時間評審代碼。

對代碼評審做出響應最好不要超過一個工作日。

如果遵循這些原則,那麼一個典型的CL在一天內(如果需要的話)可以進行多輪評審。

速度和中斷

有一種情況,即如果你正在集中精力完成手頭的任務,比如寫代碼,那就不要打斷自己去做代碼評審。研究表明,開發人員被中斷之後可能需要很長時間才能恢復到之前的狀態。因此,從團隊整體上看,在寫代碼時打斷自己比讓另一個開發人員等待代碼評審要付出更大的代價。

所以,對於這種情況,可以等到你手頭工作可以停了再開始代碼評審。可以是在完成手頭的編碼任務之後,午飯後,會議結束後,休息結束後等。

快速響應

我們所說代碼評審速度指的是響應時間,而不是CL完成整個評審過程並提交到代碼庫所需的時間。理想情況下,整個評審過程也應該是很快的,但單次評審請求的響應速度比整個過程的響應速度更重要。

有時候可能需要很長時間才能完成整個評審過程,但在整個過程中評審人員的快速響應可以極大減輕開發人員對“慢”評審的沮喪感。

如果你太忙了,可以先向開發人員發送一個響應,讓他們知道你什麼時候可以開始評審,或者建議讓其他可以更快做出響應的評審人員來評審代碼,或者提供一些初步反饋。

最重要的是評審人員要花足夠的時間進行評審,確保代碼符合標準。但不管怎樣,最好響應速度還是要快一些。

跨時區代碼評審

在進行跨時區代碼評審時,試着在開發人員還在辦公室的時候做出響應。如果他們已經回家了,那麼最好可以確保他們在第二天回到辦公室時可以看到代碼評審已經完成。

帶有註解的LGTM

爲了加快代碼評審速度,對於以下兩種情況,評審人員應該給出LGTM(Look Good to Me,沒有問題)或者通過,即使他們在CL中留下了未解決的問題:

  • 評審人員確信開發人員將會處理好評審人員給出的建議和意見。
  • 其餘的改動是次要的,不一定要求開發人員完成。

當開發人員和評審人員處於不同的時區時,最好可以使用帶有註解的LGTM,否則開發人員可能需要等上一整天才能獲得“LGTM,批准”。

大型的CL

如果有人給你發了一個很大的代碼評審,而你不確定是否有足夠時間完成評審,通常的做法是要求開發人員把CL拆分成幾個較小的CL。這樣做通常是合理的,對評審人員來說是有好處的,即使開發人員需要做點額外的工作。

如果一個CL不能拆分成更小的CL,並且你沒有足夠的時間進行快速評審,至少要對CL的總體設計寫一些註解,併發給開發人員。評審人員的目標之一是在不影響代碼質量的情況下快速對開發人員做出響應,或者讓他們能夠快速採取進一步行動。

持續改進代碼評審

如果你遵循了這些指導原則,並且對代碼評審過程嚴格要求,你會發現,隨着時間的推移,整個代碼評審過程會變得越來越快。開發人員知道爲了保證代碼質量需要做些什麼,並從一開始就向你發送非常棒的CL,這樣評審所需的時間就會越來越少。評審人員也學會了如何快速做出響應。但不要爲了提高評審速度而犧牲代碼評審標準或質量——從長遠來看,這樣做並不會讓任何事情變得更快。

緊急情況

在一些緊急情況下,CL必須非常快速地通過整個評審過程,在質量方面會有些許的放鬆。請參看這些“緊急情況”,看看哪些符合緊急情況標準,哪些不符合。

評審註解

概要

  • 禮貌。
  • 解釋你的理由。
  • 給出明確的方向,指出問題,並讓開發人員決定如何在兩者之間做出權衡。
  • 鼓勵開發人員簡化代碼,或者添加代碼註釋,而不只是讓他們解釋代碼的複雜性。

禮貌

一般來說,禮貌和尊重是很重要的。一個是要確保你的評論是針對代碼而不是針對開發人員。你不一定要一直這麼做,但當你想說一些可能會讓開發人員感到激動或有爭議的話時,絕對有必要這麼做。例如:

不好的說法:“爲什麼你要在這個地方使用線程,這樣做顯然不會獲得任何好處”。

好的說法:“在這裏使用併發模型增加了系統複雜性,但我看不到任何實際的性能好處,所以這段代碼最好使用單線程,而不是多線程”。

解釋理由

從上面的正面示例可以看出,這樣有助於開發人員理解你爲什麼要給出這些建議。你並不一定總是要在評審中提供這些信息,但如果你能夠爲你的意圖、所遵循的最佳實踐或你的建議將如何改進代碼質量給出更多的解釋會更好。

給予指導

一般來說,修復CL是開發人員的責任,而不是評審人員的責任。你不需要爲開發人員提供詳細的解決方案或者爲他們寫代碼。

不過,這並不意味着評審人員就不應該幫助開發人員。你最好可以在指出問題和給予指導之間做出權衡。指出問題,並讓開發人員做出決策,這樣有助於開發人員學到東西,並讓代碼評審變得更容易。這樣還可以產出更好的解決方案,因爲開發人員比評審人員更瞭解代碼。

不過,有時候直接給出指令、建議或代碼會更有用。代碼評審的主要目的是獲得儘可能好的CL。第二個目的是提高開發人員的技能,這樣以後需要的評審就會越來越少。

接受註解

如果你要求開發人員解釋一段你不理解的代碼,他們通常會去重寫代碼,並把代碼寫得更清晰。有時候在代碼中添加註解也是一種恰當的做法,只要它不只是用來解釋太過複雜的代碼。

不要只是把註解寫在代碼評審工具裏,因爲這對於將來要閱讀代碼的人來說並沒有多大幫助。只有少數情況可以接受這種做法,例如,你對評審的東西不太熟悉,而開發人員的解釋卻是很多人所熟知的。

代碼評審回推

有時候,開發人員會回推代碼評審。他們可能不同意你的意見,或者他們抱怨你太嚴格了。

誰是對的?

如果開發人員不同意你的意見,先花點時間想一下他們是不是對的。通常,他們比你更熟悉代碼,所以可能對代碼的某些方面更瞭解。他們的論點有道理嗎?從代碼質量角度來看,他們的回推是有道理的嗎?如果是,就讓他們知道他們是對的,這個問題就解決了。

然而,開發人員並不總是正確的。在這種情況下,評審人員要進一步解釋爲什麼他們的建議是正確的。

如果評審人員認爲他們的建議可以改善代碼質量,並認爲評審所帶來的代碼質量改進值得開發人員做出額外的工作,那麼他們就應該堅持。改善代碼質量往往是由一系列的小步驟組成的。

有時候你需要花很多時間反覆解釋,但要始終保持禮貌,並讓開發人員知道你知道他們在說什麼。

激動的開發人員

有時候,評審人員會認爲如果他們堅持要開發人員做出改動,會讓開發人員感到不安。開發人員有時候確實會感到沮喪,但這種感覺通常都很短暫,之後他們會非常感謝你幫助他們提高了代碼質量。如果你在評審過程中表現得很有禮貌,開發人員一點都不會感到不安,這種擔心可能是多餘的。通常,令開發人員感到不安的是你寫註解的方式,而不是你對代碼質量的堅持。

稍後再解決

一種常見的回推原因是開發人員希望儘快完成任務。他們不想經過一輪又一輪的代碼評審,他們說他們會在後續的CL中解決遺留問題,你現在讓CL通過就可以了。一些開發人員會做得很好,他們在提交CL後立即就開發後續的CL。但經驗表明,開發人員開發原始CL的時間越長,他們進行後續修復的可能性就越小。除非開發人員在提交CL之後立即進行修復,否則在通過之後通常不會再去做這件事情。這並不是因爲開發人員不負責任,而是因爲他們有很多工作要做,而修復工作通常會被遺忘。所以,最好讓開發人員馬上把CL修復掉。

如果CL引入了新的複雜性,在提交之前必須將其清理掉,除非是緊急情況。如果CL暴露了一些目前還無法解決的問題,開發人員需要把bug記錄下來,並將其分配給自己,這樣它就不會被遺漏。他們還可以在代碼中加入TODO註釋,指向已經記錄好的bug。

抱怨評審太嚴格

如果你之前的代碼評審很放鬆,然後突然變得嚴格起來,可能會引起一些開發人員的抱怨。不過沒關係,加快代碼評審速度通常會讓這些抱怨逐漸消失。

有時候,這些抱怨可能需要幾個月的時間才能消除,但開發人員到最後通常會看到代碼評審的價值,因爲他們看到了嚴格的代碼評審有助於產出優秀的代碼。有時候,抗議最大聲的人甚至會成爲你最堅定的支持者。

解決衝突

如果你遵循了上述方法,但仍然會在評審過程中遇到無法解決的衝突,請再次參閱代碼評審標準,瞭解那些有助於解決衝突的指導原則。

原文鏈接:https://google.github.io/eng-practices/review/reviewer/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章