譯者 | 劉汪洋
審校 | 重樓
反饋就像一塊牛排 - 如果太生,沒(méi)有人喜歡;但如果過(guò)熟,難以下咽。
(ChatGPT)
通過(guò)不斷審查他人代碼,你不僅可以提升自己的技能,對(duì)你的職業(yè)發(fā)展也有很大好處。不僅可以幫助別人成長(zhǎng),也能為你所在的公司創(chuàng)造價(jià)值。
在本文中,我們要探討代碼審查的好處,以及一些在審查過(guò)程中應(yīng)遵循的原則,如與同事的互動(dòng)等。
名詞解釋
代碼審查:對(duì)作者代碼分支的書面反饋過(guò)程。
Pull Request:GitHub 上用于展示新分支與主分支之間差異的術(shù)語(yǔ),你可以在其中發(fā)表評(píng)論。
代碼審查?是否已經(jīng)過(guò)時(shí)?
如果你是一名敏捷開發(fā)者,你可能會(huì)懷疑代碼審查的必要性,可能會(huì)有這樣的觀點(diǎn):
- 我們?nèi)绻恢边M(jìn)行結(jié)對(duì)編程,就沒(méi)有必要再做正式的代碼審查了。因?yàn)檫@樣就不會(huì)有一個(gè)“陌生人”在你已經(jīng)完成所有工作并花費(fèi)了大量時(shí)間之后,才來(lái)評(píng)價(jià)你的代碼,而他對(duì)你的代碼的上下文可能一無(wú)所知。
- 代碼的正確性已通過(guò) TDD (測(cè)試驅(qū)動(dòng)開發(fā))方法進(jìn)行了驗(yàn)證。
- 語(yǔ)法和風(fēng)格可以由 linter 自動(dòng)檢查。
這些考慮無(wú)疑是有道理的。
但:
代碼審查本質(zhì)上是一種團(tuán)隊(duì)成員之間和不同團(tuán)隊(duì)之間的知識(shí)共享機(jī)制。
代碼審查的好處
一旦你不再把代碼審查視為負(fù)擔(dān)或者無(wú)聊的任務(wù),你會(huì)發(fā)現(xiàn),代碼審查能帶來(lái)很多好處。
為了支持這個(gè)觀點(diǎn),我提出了一個(gè)用于在同事的 pull request 中提供反饋的框架。
W3H:即“為什么(why)、做什么(what)、何時(shí)(when)以及如何做(how)”
盡管我并非縮寫詞的狂熱愛(ài)好者,但我還是創(chuàng)建了一個(gè) W3H 的縮寫詞,旨在概括我們?cè)诮佑|新的代碼時(shí)所需要考慮的關(guān)鍵問(wèn)題。
為什么(why)
代碼審查可能是由于你的組織內(nèi)部的 CI/CD 流程強(qiáng)制推動(dòng)的,因此,“為什么”的問(wèn)題可以簡(jiǎn)單地回答為“因?yàn)槲冶恢甘疽@樣做”。
然而,有更有價(jià)值的問(wèn)題值得我們思考:“為什么代碼審查如此重要?”或者說(shuō),“為什么我應(yīng)該主動(dòng)去進(jìn)行代碼審查?”
首先,代碼審查對(duì)審查者和代碼作者來(lái)說(shuō)都是一個(gè)提升自我技術(shù)水平的好機(jī)會(huì)。代碼作者可以得到有效的反饋和建議,有助于其技術(shù)水平的提升。審查者則可以從中學(xué)習(xí)新的編程技巧和習(xí)語(yǔ)。
此外,作為開發(fā)者,你還可以:
- 更快地理解新的代碼庫(kù)。
- 有更多的機(jī)會(huì)進(jìn)行團(tuán)隊(duì)內(nèi)交流,尤其在團(tuán)隊(duì)成員分布在不同地理位置的情況下。
- 發(fā)現(xiàn)其他團(tuán)隊(duì)工作中可能存在的重復(fù)問(wèn)題。
- 在代碼庫(kù)中推動(dòng)最佳實(shí)踐的實(shí)行。
- 提升你在其他工程師和管理者心中的專業(yè)形象和知識(shí)儲(chǔ)備。
- 提高你的溝通技巧(我們將在“如何”一節(jié)中詳細(xì)討論)。
- 結(jié)識(shí)新朋友!
做什么(what)
我們?nèi)绾味x“優(yōu)秀的”代碼?
在對(duì)別人的代碼發(fā)表任何評(píng)價(jià)之前,你需要明確好代碼的基本標(biāo)準(zhǔn)。
以下是一些通用的原則:
- 代碼中不應(yīng)存在 明顯的 錯(cuò)誤,例如變量名的拼寫錯(cuò)誤,或者不規(guī)范的代碼縮進(jìn)。當(dāng)我審查一個(gè)我并不太熟悉的模塊的 PR 時(shí),我通常會(huì)從這些基礎(chǔ)的檢查開始。
- 無(wú)論代碼完成的任務(wù)有多復(fù)雜,代碼本身都應(yīng)該是 結(jié)構(gòu)清晰 和 易于理解。
- 代碼中不應(yīng)存在嚴(yán)重的 性能問(wèn)題,例如,一個(gè)可以通過(guò)一次循環(huán)讀取完成的列表被多次讀取。這在移動(dòng)應(yīng)用開發(fā)中尤其重要,因?yàn)檫@可能導(dǎo)致不必要的電池電量消耗。
- PR 只應(yīng)該修改完成任務(wù)(新特性,錯(cuò)誤修復(fù),重構(gòu))所必需的 文件。這不僅可以減輕審查者的工作壓力,而且如果出現(xiàn)了重大的生產(chǎn)問(wèn)題,也會(huì)更容易找到問(wèn)題并進(jìn)行回滾。如果代碼涉及多個(gè)方面,建議作者將其拆分為兩個(gè)或更多的審查。
- 清晰理解更改的目標(biāo)。PR 的描述應(yīng)該清晰地說(shuō)明更改的內(nèi)容或者鏈接到包含詳細(xì)信息的外部文檔(例如 Jira 或 Trello 工單)。理解了更改的目標(biāo)后,再檢查更改是否滿足了預(yù)設(shè)的要求。
- 如果 PR 是用來(lái)修復(fù)一個(gè)錯(cuò)誤,修復(fù)方案還應(yīng)該包含一組測(cè)試,這樣可以針對(duì)特定的場(chǎng)景進(jìn)行測(cè)試,并避免相同的錯(cuò)誤在未來(lái)再次發(fā)生。
何時(shí)(when)
你應(yīng)該在什么時(shí)候進(jìn)行代碼審查?
我通常每周安排兩次,每次約 30 分鐘的時(shí)間,審查其他團(tuán)隊(duì)的 PR,這些與我自己團(tuán)隊(duì)的工作并無(wú)直接關(guān)聯(lián)。如果我團(tuán)隊(duì)有緊迫的項(xiàng)目截止日期,可能我會(huì)減少審查的時(shí)間;如果是工作相對(duì)清閑,并且有一些評(píng)論引發(fā)了大量的討論,我可能會(huì)投入更多的時(shí)間。
在我剛開始接觸 iOS 開發(fā)時(shí),我花在代碼審查上的時(shí)間和學(xué)習(xí) Swift 和 iOS 的時(shí)間幾乎一樣多。這使我能夠快速地熟悉新的代碼庫(kù),語(yǔ)言習(xí)語(yǔ),最佳實(shí)踐,以及了解項(xiàng)目中的關(guān)鍵人物。
我通常在面對(duì)學(xué)習(xí)新的大型代碼庫(kù)時(shí),會(huì)立即開始進(jìn)行代碼審查(這是作者有效學(xué)習(xí)和了解新代碼庫(kù)的一種方法)
如何(how)
編寫大量的評(píng)論可能會(huì)讓你感到枯燥乏味,因此你可能在表述上變得過(guò)于直白。書面交流與口頭交流有所不同,我建議你遵循以下建議:
友善待人
這一點(diǎn)無(wú)需多說(shuō)。通常情況下,當(dāng)我向我不太熟悉的人寫評(píng)論時(shí),我會(huì)首先用一個(gè)簡(jiǎn)單的 "你好 " 打招呼。如果你發(fā)現(xiàn)有什么項(xiàng)目缺失,不要直接用 "這是不完整的" 來(lái)表示,你可以詢問(wèn) "這里為什么會(huì)缺失?"
注意細(xì)節(jié)
嘗試在關(guān)鍵的和不那么關(guān)鍵的修改之間找到平衡,這需要你的經(jīng)驗(yàn)來(lái)指導(dǎo)。我們不希望因?yàn)橐粋€(gè)小的空格問(wèn)題而阻塞了一個(gè)重要功能的實(shí)現(xiàn)或者 bug 的解決!
詢問(wèn)作者的意見(jiàn)
如果你提出的改動(dòng)不顯著,或者你提出了重構(gòu)的建議,那么最好在你的評(píng)論結(jié)尾處詢問(wèn)一下 "你怎么看?" 或者 "你對(duì)此有什么看法?"
有時(shí)需要權(quán)衡
有些項(xiàng)目比其他項(xiàng)目更為重要。盡量不要因?yàn)樾〉男薷亩鴪?jiān)持不懈,以免造成發(fā)布延遲。你可以與作者達(dá)成共識(shí),以后再創(chuàng)建一個(gè)"打磨"分支。然而,我常說(shuō):
以后 == 永不(later == never)
不要做假設(shè)
你正在審查一個(gè) PR 并發(fā)現(xiàn)了一個(gè)明顯的錯(cuò)誤,而這段代碼的開發(fā)者的職稱是"初級(jí)"。你可能會(huì)假設(shè)這個(gè)錯(cuò)誤是由于他們的經(jīng)驗(yàn)不足,或?qū)σ恍┗靖拍畹睦斫獠粔蛏钊搿R部赡苁撬麄兇颐ν瓿傻?,或者在度過(guò)了一天的勞累后提交的?;蛘摺赡苡幸恍┪覀兾茨芸吹降?、合理的原因在背后。如果有疑問(wèn),告訴作者: "可能是我漏掉了什么,但是……"
保持個(gè)人偏好的開放性
當(dāng)你在代碼審查中建議更改時(shí),確保這些更改是對(duì)代碼的實(shí)實(shí)在在的改進(jìn),而非審查者的個(gè)人偏好,這些偏好可能并不符合公司或行業(yè)的最佳實(shí)踐。當(dāng)我提出這樣的更改時(shí),我會(huì)說(shuō): "這是個(gè)人偏好的問(wèn)題,但如果你愿意,可以試試<代碼更改>"
贊美作者
“看起來(lái)很好(LGTM)” 可能會(huì)被解讀為“我草率地審查了你的 PR”。對(duì)我來(lái)說(shuō),看到這個(gè)還好,但是,如果你真的認(rèn)為代碼寫得很好,那就在 PR 中大膽地表?yè)P(yáng)。以下是值得贊揚(yáng)的原因:引入了新的炫酷功能,或者進(jìn)行了復(fù)雜但結(jié)構(gòu)良好的重構(gòu)。
如果 pull request 只包含一個(gè)簡(jiǎn)單的顏色更改或函數(shù)參數(shù)的添加,那么過(guò)度的贊美可能會(huì)被誤認(rèn)為是諷刺 。請(qǐng)選擇適當(dāng)?shù)恼Z(yǔ)言表達(dá)。
副作用
盡管你持有善意,或者在代碼審查過(guò)程中為項(xiàng)目貢獻(xiàn)良多,有時(shí)你的行動(dòng)可能仍會(huì)引發(fā)他人的反感。他們可能按照你的建議進(jìn)行修改,但可能并未對(duì)你的審查表示感激。
有些人可能會(huì)駁斥你的建議
你的建議可能錯(cuò)誤,無(wú)效,或者觸碰到了作者的自尊心,使他們認(rèn)為修改代碼就等于否定自己。這沒(méi)有關(guān)系。然而,如果你認(rèn)為某段代碼可能會(huì)導(dǎo)致嚴(yán)重的問(wèn)題或性能影響,你應(yīng)該考慮邀請(qǐng)其他開發(fā)者一起討論,例如在評(píng)論中@他們,引導(dǎo)他們參與討論。更好的做法是,嘗試直接通過(guò)電話或面對(duì)面的方式進(jìn)行討論。
不要期待你的付出一定會(huì)得到回報(bào)
你或許為許多代碼審查和批準(zhǔn)做出了貢獻(xiàn),幫助審查了他人的代碼,但當(dāng)你自己的代碼需要審查的時(shí)候,不要期待你的新 pull request 一定會(huì)受到關(guān)注。
你可能會(huì)收獲友情
去年,我為了更好地熟悉某個(gè)代碼庫(kù),我對(duì)一個(gè)新接手的代碼庫(kù)進(jìn)行了持續(xù)的代碼審查。我與許多開發(fā)者進(jìn)行了積極的討論,最終,對(duì)倉(cāng)庫(kù)中的一些模塊進(jìn)行了改進(jìn)。這是作者和我——這位新來(lái)的代碼審查者的共同努力的結(jié)果!我結(jié)識(shí)了一些愿意改進(jìn)他們代碼、并始終對(duì)新建議和學(xué)習(xí)保持開放態(tài)度的優(yōu)秀人才。季度結(jié)束時(shí),我向他們請(qǐng)求正式反饋,收到了非常積極的回應(yīng)。我們可以稱這種關(guān)系為"聯(lián)系",甚至我愿意稱它為"友誼"。
結(jié)語(yǔ)
在這篇文章中,我們探討了持續(xù)進(jìn)行代碼審查的諸多優(yōu)點(diǎn)。 我推廣的 W3H 方法,就是一種給他人的代碼提供反饋的流程。
在我所工作的 Expedia Group?,我經(jīng)常應(yīng)用這種方法,公司也倡導(dǎo)并正式提出了"有意識(shí)的包容"這一價(jià)值觀。
本文要點(diǎn)總結(jié)如下:
- 認(rèn)識(shí)到審查他人代碼的好處(比如建立聯(lián)系,熟悉新的代碼庫(kù),學(xué)習(xí)新的編碼風(fēng)格等)。
- 建立持續(xù)審查的習(xí)慣。
- 提供超越你團(tuán)隊(duì)范圍的反饋。
- 避免過(guò)度假設(shè)。
- 不要期待每個(gè)人都會(huì)同等地關(guān)注你的 pull request。
- 保持友善。
你公司的代碼審查流程是怎樣的?你在工作中是否真正感受到代碼審提高了代碼質(zhì)量?歡迎發(fā)表你的看法。
譯者介紹
劉汪洋,51CTO社區(qū)編輯,昵稱:明明如月,一個(gè)擁有 5 年開發(fā)經(jīng)驗(yàn)的某大廠高級(jí) Java 工程師,擁有多個(gè)主流技術(shù)博客平臺(tái)博客專家稱號(hào)。
原文標(biāo)題:The Importance of Being a Code Reviewer,作者:Carlo Sales