這是我文章的後半部分,關於如何在 Code review 中進行良好的溝通,避免陷入一些潛在的陷阱。這裏,我會着重於介紹一些技巧,讓你的 Code review 可以順利完成,避免磕磕碰碰。cookie
在第一篇文章中,我介紹了不少基礎的東西,因此我更推薦從那裏開始讀。固然若是你沒有耐心,那麼這裏用一句話歸納一些:一個好的代碼評審者,不只須要找出代碼中的 bug,還須要提供認真負責的反饋來讓團隊夥伴獲得能力上的提高。網絡
我最糟糕的一次 Code review 經歷,是和一個叫 Mallory 的前同事有關。她早我幾年加入公司,但最近才轉到個人團隊來。函數
當我收到 Mallory 的第一份變動列表,裏面的代碼是很粗糙的。她以前徹底沒有寫過 Python 代碼,並且她的代碼是基於一個我維護的笨重的老系統上開發的。post
我很盡職地記錄下了全部我找到的問題,一共有 59 個。按照其它 Review 文章的標準來看,我作得很不錯。我找到了如此多的錯誤,因此我確定是個優秀的評審者。debug
過了幾天後,Mallory 給了我一份更新過的變動列表,已經針對我以前的 Review 修改過了。她修復了一些簡單的問題,好比拼寫錯誤、變量重命名等等,但她拒絕修改任何更高層次的問題,好比她的代碼對於錯誤的輸入會產生不肯定的行爲,還有她的一個函數嵌套了 6 層邏輯。她拒絕修改這些東西,輕蔑地解釋說,這些東西不值得花工做時間去修復。設計
我看到這些,心情有些惱怒,因此又寫了一輪新的評論。個人語氣很專業,可是不可避免的有些火藥味。「你能解釋一下,爲何咱們須要對於錯誤的輸入會產生不肯定的行爲嗎?」正如你所猜的那樣,Mallory 的回覆比以前更執拗了。3d
一週後的星期二,Mallory 和我依然停留在第四輪相同的 Review 上。我前一天晚上提交了個人新一輪評論,但實際上我是故意拖了一天才提交的,由於當她讀這些評論時,我不想跟她呆在一個屋子裏。code
整個早上,我一直以爲胃不舒服,由於我恐懼又要開始新一輪 Review。當我吃完午餐回來時,發現 Mallory 已經提交了新的代碼,但她已經不在位置上了,我估計她也不想呆在我身邊看着我審查這些更新。cdn
我讀了代碼以後,個人心劇烈地跳動,由於真的被她惹怒了。我馬上開始奮力敲擊個人鍵盤,指出她既沒有按照個人要求做出修改,也沒有用任何理由說服我批准這個變動列表。視頻
咱們就這樣毅種循環(誤)了整整三個星期,而代碼幾乎沒怎麼改動過。
幸運的是,咱們團隊裏最高級別的工程師,Bob,幫咱們打破了這個循環。他剛剛休完長假回來,驚訝地發現咱們兩個正在把 Code review 相互扔來扔去。他意識到這是一個僵局,因此請求咱們讓他接管這個 Review,咱們都贊成了。
Bob 在最開始時,先讓 Mallory 建立了幾個新的變動列表,把咱們倆以前爭吵歷來沒有提到過的兩個小型庫分隔開,每一個大概 30 - 50 行。Mallory 作完以後,Bob 馬上批准了。
而後,Bob 回到主要的變動列表上來,這個列表如今已經被裁剪成 200 行左右的代碼。他給了一些小建議,Mallory 響應修改了。再而後,他批准了這個變動列表。
Bob 的整個 Review 就花了兩天。
你或許已經發現,這兒產生的衝突其實並非關於代碼的。Code review 裏提出的問題都是合情合理的,它們也明顯能夠被溝通能力強的團隊成員解決。
這是一個很不愉快的經歷,但我很高興能回想起來,由於它讓我從新思考了個人 review 方法是否恰當,並從中找到能夠改進的地方。
下面,我會介紹一些技巧,以免相似的不愉快經歷。而後我會回到 Mallory 這個例子上,解釋爲何我以前的方法是很差的,而爲何 Bob 作得那麼好。
即便你的團隊夥伴,理論上,想找到任何機會來讓他們的代碼變得更好,但他們的耐心也依然是有限的。當你通過一輪又一輪的 review 以後,依然不願批准變動,他們的心情會變得很沮喪,由於你老是在不停地想各類方法來改進他們的代碼。
我我的會把代碼分幾個級別,從 A 到 F。當我收到一份評分爲 D 的變動列表,我會嘗試幫助做者把它改進到 C 或者 B-。不是完美的,但足夠好了。
固然,把一份 D 評分的代碼改進到 A+ 在理論上是可能的,但它可能會須要八輪 review。最後,代碼做者會怨恨你,而且將來不再給你任何 review 的機會了。
你可能會想,「若是我批准了 C 等級的代碼,那最後代碼庫裏不就全都是 C 等級的代碼了嗎?」 其實否則,我發現,若是我幫助團隊成員從 D 改進到 C,那麼他們的下一份變動列表就會從 C 等級開始。幾個月後,他們給個人 review 都是從 B 等級開始了,這些改進以後就會變成 A 等級。
固然有一個特例,那就是 F 等級,這個等級是保留給一些寫得太爛的代碼的,通常是功能不正確,或者寫得太錯綜複雜,以至於你沒法判斷代碼的正確性。你駁回它的惟一理由應該是,它通過了幾輪 review 以後,依然沒有任何改進。這時,請參考下面的關於僵局的部分。
當你發現了做者不少相同的問題,不須要把每一處都標明,你確定不想把一樣的評論重複 25 次,代碼的做者也不想讀 25 次相同的評論。
對於一樣的問題,只須要重複註明 2- 3 次,而後剩下的,就直接評論說讓做者修復相似的問題就行了,而不是去註明每一處問題。
有一個我常常見到的反模式,那就是評審者會評論變動列表附近的一些代碼,並要求做者修改它們。做者修改後,評審者一般會以爲代碼確實更好了,但和別處的代碼有一些不一致,因而又要求進一步的更改,以及再進一步的更改。最後讓變動列表擴展得很大,包括了不少不相關的東西。
若是一隻飢餓的小老鼠出如今你的家門口,你可能會想給他一塊餅乾。若是你給他一塊餅乾,它又會想要一杯牛奶。而後它會想要一面鏡子,以確保它的鬍子上沒有粘上牛奶,而後它會要一把剪刀給本身剪剪鬍子...
—— Laura Joffe Numeroff, If You Give a Mouse a Cookie
因此有一條準則:若是變動列表沒有涉及到這一行,那它就是超出 review 範圍的。
下面就是一個例子:
即便你失眠了一整晚,被這個魔術數字和荒唐的變量名所困擾,它也是超出範圍的。即便寫下這一行代碼的人就是本次 review 的做者,它也是超出範圍的。若是它真的很是糟糕,那也請你提一個 bug,或者提交你的修復,但不要把它放到本次 review 的範疇中來。
固然有個例外,那就是當變動列表影響了周圍的代碼時,好比:
這個例子裏,須要讓做者把 ValidateAndSerialize
重命名爲 Serialize
。雖然這超出了變動列表的範圍,但它會致使不正確的命名。
當我沒發現問題,但發現了範圍外的某個很容易修復的問題時,我會不遵照這個規則。在這種狀況下,我會明確地表示,做者能夠徹底無視它。
當你收到了一個約 400 行代碼的變動列表,你應該鼓勵做者把它切分到更小的塊。超過得越多,你就越應該打回這個變動列表。我我的拒絕 review 任何超過 1000 行的變動列表。
做者對於 「切分變動列表」 這件事可能會有些怨言,由於這是個很乏味的任務,因此做爲評審者的咱們最好爲做者劃出要切分邊界,以減輕他們的負擔。最簡單的一種狀況是,變動列表涉及到多個文件,這時即可以按文件切分爲多個更小的變動列表。固然也會有更復雜一些的狀況,這時也許須要找到抽象層次比較低的函數或者類,而後要求做者把它們移動到另外一個變動列表裏,等另外一個變動列表合併以後,再回來看如今的這一個。
當代碼寫得很糟糕時,就更應該要求切分。由於這時 review 的難度隨着代碼的長度呈指數級增加。Review 多個 300 行的變動,比 review 單個 600 行的變動,要好得多。
大多數評審者都只關注代碼中的錯誤,但忘記了 review 也是一個促進積極行爲的好機會
好比,你正在 review 的這個做者是個文檔寫得很掙扎的人,但你卻看到了一條清晰、簡潔的註釋,這時請表揚他一下。當你告訴他們什麼作得對時,他們會進步得更快,而不是等着他們犯錯才告訴他們。
你不須要刻意地去表揚做者,每當我看到變動列表中有任何亮點,我都會告訴做者:
若是做者是個最近才加入團隊的萌新,他們在 code review 的時候可能會感到緊張和焦慮。爲了緩解這種情緒,你須要真誠地表揚他們,證實你是支持他們的同伴,而不是殘酷無情的代碼守門員。
有些評審者會有一個錯誤的觀念,他們總會一直不願給出批准,直到他們看到每一條評論都獲得了修復。這增長了沒必要要的工做,也浪費了做者和評審者的時間。
當有下面這些狀況時,能夠直接批准變動:
我以前看到過有些評審者一直不願給出批准,由於做者在最後的評論後就沒再有任何迴應了。請不要這樣作,這會讓做者以爲你認爲他們連加一個標點符號都應該被監視着。
當還剩一些很重要的問題沒修改時,直接給出批准可能會很危險。根據個人經驗,大約有 5% 的機率會發生這樣的事,要麼做者誤解了最後一輪評論的意思,要麼他徹底沒看到。爲了解決這種狀況,我會去檢查一遍要批准的變動。若是真的是由於罕見的溝通不順暢,我要麼會繼續跟進做者,要麼會本身建立一個新的變動來修復問題。在 5% 的狀況下增長少許的工做,要好過於,在另外 95% 的狀況下加入更多沒必要要的精力和拖延。
在 code review 中,最糟糕的狀況就是僵持:若是沒有進一步更改的話,你不想批准這個變動列表,而代碼的做者拒絕修改它。
下面這些指標,表示你正在走向僵持狀態:
直接面對面交流或者經過網絡視頻。文字交流最後會讓你忘了你對話的是一個真正的人,很容易讓你想象你的同伴是一個來自無能與執拗之地的人。面對面的交流將會打破這個魔咒。
可能會進入僵持狀態的 code review 可能在更早的時候就有一些徵兆了。大家如今是否是在一些事情上爭吵,而這些事情本應該在設計審查時討論的?大家有設計審查嗎?
若是分歧的根源是高層次的設計問題,那就應該是由更大的團隊來權衡利弊,而不是交給 code review 的兩我的。你應該和做者在 review 中討論,同時開放給團隊其餘成員,以設計審查的形式讓他們也進來討論。
你和你的同伴在 review 中僵持得越久,就越有害於大家之間的關係。若是問題一直沒有獲得解決,那麼你最好選擇讓步,或者把問題升級。
學會衡量給出批准的成本。當你老是接受低質量代碼時,你的軟件質量固然不可能好,但當你和你的團隊夥伴痛苦地工做,以致於大家沒法在一塊兒愉快地合做時,軟件質量一樣也不會有任何提升。這個時候應該想一想,若是你批准了變動列表,結果會有多糟?這些代碼會破壞重要的數據嗎?它是一個後臺進程,而且只有當最壞的狀況發生時才須要開發人員對它進行 debug 嗎?若是更偏向於後者,那麼能夠考慮批准變動,以便讓你和你的團隊夥伴繼續工做。
若是你不想作出讓步,請告訴做者,將這裏的爭議升級到團隊的管理者或者技術負責人那裏,把 review 從新分配給另外的評審者。即便以後得出的結果和你以前的意見相悖,也要接受它。繼續執拗下去只會致使更很差的結果,讓你看起來很不專業。
一次棘手的 review 中的爭吵,真正的重點極可能並非代碼,而是人和人的關係。若是你陷入了僵局或者即將陷入僵局,而且不去解決那些潛在的衝突,那麼這種狀況會一直髮生下去。
還記得我和 Mallory 的那次 code review 嗎?爲何個人 review 花了整整三週,而且成效甚微,而 Bob 僅花了兩天就搞定了?
這是 Mallory 在咱們團隊的第一次 review。我以前並無考慮到她可能會感受本身受到評判,產生戒備心。我應該在最開始的時候給出一些高層次的評論,讓她不會由於大量的評論而感到挫折。
我應該更好地向她代表,個人工做不是去阻礙她的工做,而是幫助她變得更好。我應該給出更多的代碼示例,以及在她的變動列表中給出更多的積極因素。
個人自尊心也過多地影響了此次 review。要知道我花了一全年的時間,才把這個老系統修復到健康狀態,忽然有了一個新人開始在這個系統上作些事情,而她就再也不須要考擔憂這些我曾經擔心的問題了嗎?我認爲這是一種冒犯,也就是這種態度產生了反作用。我應該在全部的評論上都保持一個客觀的態度。
最後一點,我讓這個僵局持續過久了。在幾輪 review 事後,咱們都應該很清楚,咱們沒有任何有意義的進展。這時我應該主動作出改變,好比主動當面交流,以解決深層次的衝突,或者把它升級到咱們的上級那裏。
Bob 第一步把 review 切分的作法很是有效。這個 review 已經花費了痛苦的三週時間,突然,其中的兩塊代碼被合併了。這讓 Mallory 和 Bob 都感受到激勵,由於它產生了向前的動力。儘管剩下的代碼裏依然存在問題,但它變成了一個更小的,更好管理的變動列表。
Bob 沒有嘗試在 review 的過程當中把代碼改形成天衣無縫的,他可能已經意識到那些代碼裏我以爲沒法忍受的問題,但他也一樣意識到 Mallory 會待在咱們團隊一段時間。對於某些特殊狀況的靈活處理,讓他能夠在長時間內幫助 Mallory 提高質量。
在我發表這篇文章的前半部分以後,有些讀者對我鼓勵的溝通風格產生了一些質疑。有人以爲它太屈尊卑微了,有人以爲這種風格太委婉了,會產生誤解。
這樣的反饋是合情合理的。對於一樣的一條評論,有人可能會以爲它很粗魯無禮,而有的人卻會以爲它很簡潔高效。
當你在 review 代碼時,你會面臨不少選擇:該關注什麼,怎麼寫反饋,何時給出批准。對於這些選擇,你是否遵循個人建議其實不重要,只要你記得有這些選擇就好。
沒有人能夠給你一個完美的 code review 指南。什麼是最好的技巧,取決於代碼做者的性格、你和他們的關係還有大家團隊的文化。不斷思考你的 code review 會產出什麼結果。當你遇到任何緊張形勢時,先退一步思考爲何會發生。關注你 review 的質量。若是你以爲代碼質量很難提高到符合你的標準,應該思考 review 的那些過程阻礙了你,以及如何解決它們。
最後祝好運,但願你能用人類的方式進行 code review。