戰(zhàn):構(gòu)建透明可追溯的代碼審查協(xié)作機(jī)制)
1. 從標(biāo)題拆解 open-code-review 的真實(shí)意圖第一次看到open-code-review這個(gè)標(biāo)題我腦子里蹦出來(lái)的第一個(gè)判斷是這大概率不是一個(gè)單純的“代碼審查工具”而是一套圍繞開(kāi)放協(xié)作場(chǎng)景設(shè)計(jì)的審查流程或框架。為什么這么說(shuō)因?yàn)椤皁pen”這個(gè)詞放在“code review”前面通常意味著兩件事——要么是審查過(guò)程本身對(duì)社區(qū)透明開(kāi)放要么是這套方案本身是開(kāi)源的、可被任何人參與和改造的。結(jié)合當(dāng)前研發(fā)團(tuán)隊(duì)越來(lái)越分布式、跨時(shí)區(qū)協(xié)作的現(xiàn)狀這個(gè)標(biāo)題背后指向的核心需求其實(shí)非常明確讓代碼審查這件事從“少數(shù)人把關(guān)”變成“多人協(xié)作、過(guò)程透明、結(jié)果可追溯”的機(jī)制。我在過(guò)去幾年參與過(guò)不少團(tuán)隊(duì)的研發(fā)流程搭建發(fā)現(xiàn)一個(gè)很普遍的現(xiàn)象很多團(tuán)隊(duì)嘴上說(shuō)著要做代碼審查實(shí)際上要么是走個(gè)形式點(diǎn)個(gè)“同意”要么是審查意見(jiàn)散落在各種聊天記錄里過(guò)兩周就找不到了。open-code-review這個(gè)標(biāo)題吸引我的地方就在于它把“開(kāi)放”和“審查”綁在一起暗示了一種更系統(tǒng)化的做法。它要解決的問(wèn)題不是“怎么寫(xiě)代碼”而是“怎么讓一群人高效地對(duì)同一份代碼達(dá)成質(zhì)量共識(shí)”。這篇文章適合誰(shuí)來(lái)讀如果你是團(tuán)隊(duì)里負(fù)責(zé)搭建研發(fā)流程的人、是剛接手代碼審查規(guī)范的技術(shù)負(fù)責(zé)人、或者是一個(gè)想把自己項(xiàng)目的審查環(huán)節(jié)做得更透明的小團(tuán)隊(duì)核心成員那接下來(lái)的內(nèi)容應(yīng)該能給你不少可直接抄作業(yè)的思路。我會(huì)從整體設(shè)計(jì)思路、核心環(huán)節(jié)拆解、實(shí)操落地步驟、常見(jiàn)坑排查這幾個(gè)維度把open-code-review這件事講透。需要提前說(shuō)明的是標(biāo)題本身沒(méi)有給出具體的技術(shù)棧和實(shí)現(xiàn)細(xì)節(jié)所以我會(huì)基于一線研發(fā)流程搭建的常見(jiàn)實(shí)踐補(bǔ)充那些“一個(gè)合格從業(yè)者在這個(gè)場(chǎng)景下最可能采用的合理方案”并明確標(biāo)注哪些是補(bǔ)充推演。2. 整體設(shè)計(jì)思路與方案選型考量2.1 為什么“開(kāi)放”是代碼審查的關(guān)鍵變量傳統(tǒng)代碼審查有一個(gè)隱含假設(shè)審查者比被審查者更資深審查是一個(gè)“向上匯報(bào)”的過(guò)程。這種模式在小團(tuán)隊(duì)里勉強(qiáng)能用一旦團(tuán)隊(duì)規(guī)模超過(guò)十幾個(gè)人就會(huì)暴露出兩個(gè)致命問(wèn)題。第一審查瓶頸集中在少數(shù)幾個(gè)資深成員身上他們一旦忙起來(lái)整個(gè)團(tuán)隊(duì)的合并請(qǐng)求就全卡住了。第二審查意見(jiàn)的質(zhì)量高度依賴(lài)個(gè)人狀態(tài)同一個(gè)人今天心情好可能看得細(xì)明天趕進(jìn)度可能就草草通過(guò)。open-code-review里的“開(kāi)放”二字我理解它要打破的正是這種單點(diǎn)依賴(lài)。開(kāi)放意味著三件事審查規(guī)則公開(kāi)、審查過(guò)程可見(jiàn)、審查結(jié)果可追溯。規(guī)則公開(kāi)是指團(tuán)隊(duì)事先約定好什么樣的代碼需要重點(diǎn)看、什么樣的改動(dòng)可以快速通過(guò)而不是每次靠審查者臨場(chǎng)發(fā)揮。過(guò)程可見(jiàn)是指所有審查意見(jiàn)都留在同一個(gè)地方誰(shuí)提了什么意見(jiàn)、誰(shuí)回復(fù)了什么、最后怎么解決的一目了然。結(jié)果可追溯是指每一次合并請(qǐng)求的審查記錄都能被檢索到方便后續(xù)復(fù)盤(pán)和新人學(xué)習(xí)。我試過(guò)在一個(gè)二十多人的研發(fā)團(tuán)隊(duì)里推行這套思路最直觀的變化是新人上手速度明顯變快了。以前新人想知道“這個(gè)模塊的代碼風(fēng)格是什么樣”只能去翻老代碼猜現(xiàn)在直接看歷史審查記錄哪些寫(xiě)法被反復(fù)指出問(wèn)題、哪些模式被多次表?yè)P(yáng)清清楚楚。這就是“開(kāi)放”帶來(lái)的隱性收益——它把審查從一次性的質(zhì)量把關(guān)變成了一個(gè)持續(xù)積累的知識(shí)庫(kù)。2.2 方案選型的三個(gè)核心維度搭建一套o(hù)pen-code-review機(jī)制繞不開(kāi)三個(gè)維度的選型審查粒度、自動(dòng)化程度、協(xié)作工具。這三個(gè)維度沒(méi)有絕對(duì)的最優(yōu)解關(guān)鍵看團(tuán)隊(duì)當(dāng)前的痛點(diǎn)和成熟度。審查粒度上常見(jiàn)的有兩種極端。一種是“大顆粒審查”一個(gè)合并請(qǐng)求包含幾十個(gè)文件的改動(dòng)審查者一看就頭大最后往往只看了幾個(gè)核心文件就點(diǎn)了通過(guò)。另一種是“小顆粒審查”每個(gè)合并請(qǐng)求只改一個(gè)函數(shù)或一個(gè)配置項(xiàng)審查起來(lái)很快但合并請(qǐng)求的數(shù)量會(huì)暴增管理成本上升。我的經(jīng)驗(yàn)是單個(gè)合并請(qǐng)求的改動(dòng)行數(shù)控制在 200 到 400 行之間是比較舒服的區(qū)間超過(guò) 600 行審查質(zhì)量就會(huì)明顯下降。這個(gè)數(shù)字不是拍腦袋來(lái)的而是基于一個(gè)簡(jiǎn)單的事實(shí)人的注意力在連續(xù)審查 20 分鐘后會(huì)顯著衰減400 行左右的改動(dòng)配合適當(dāng)?shù)纳舷挛膭偤媚茉谧⒁饬谋M前審?fù)?。自?dòng)化程度上我強(qiáng)烈建議把能機(jī)器做的全部交給機(jī)器。代碼格式檢查、靜態(tài)語(yǔ)法分析、單元測(cè)試覆蓋率、依賴(lài)漏洞掃描這些都不應(yīng)該占用人工審查的時(shí)間。人工審查應(yīng)該聚焦在機(jī)器判斷不了的事情上命名是否合理、邏輯是否有邊界問(wèn)題、設(shè)計(jì)是否符合當(dāng)前架構(gòu)方向、有沒(méi)有隱藏的性能陷阱。我見(jiàn)過(guò)一些團(tuán)隊(duì)把格式問(wèn)題也拿到審查會(huì)上討論結(jié)果半小時(shí)過(guò)去了還在爭(zhēng)論縮進(jìn)用兩個(gè)空格還是四個(gè)空格這是典型的資源錯(cuò)配。協(xié)作工具的選型上核心原則是審查記錄必須和代碼倉(cāng)庫(kù)綁定。不管是用平臺(tái)自帶的合并請(qǐng)求功能還是自建一套審查系統(tǒng)審查意見(jiàn)一定要能關(guān)聯(lián)到具體的代碼行并且能在代碼變更后自動(dòng)標(biāo)記“該意見(jiàn)是否已解決”。我踩過(guò)的一個(gè)坑是早期團(tuán)隊(duì)用聊天工具做審查意見(jiàn)發(fā)在群里結(jié)果代碼改了之后沒(méi)人知道對(duì)應(yīng)的是哪條意見(jiàn)最后只能重新看一遍。后來(lái)遷移到倉(cāng)庫(kù)自帶的審查功能后這個(gè)問(wèn)題自然就消失了。2.3 這套機(jī)制適合什么規(guī)模的團(tuán)隊(duì)open-code-review不是萬(wàn)能藥它的收益和團(tuán)隊(duì)規(guī)模、協(xié)作復(fù)雜度強(qiáng)相關(guān)。我的觀察是五人以?xún)?nèi)的團(tuán)隊(duì)如果大家坐在一起口頭溝通效率可能比走正式審查流程更高強(qiáng)行上這套機(jī)制反而增加負(fù)擔(dān)。五到十五人的團(tuán)隊(duì)是收益最明顯的區(qū)間這時(shí)候口頭溝通開(kāi)始出現(xiàn)信息丟失正式審查流程能顯著降低溝通成本。十五人以上的團(tuán)隊(duì)如果沒(méi)有一套開(kāi)放的審查機(jī)制代碼質(zhì)量會(huì)隨著人員流動(dòng)快速滑坡因?yàn)橹R(shí)全在少數(shù)人腦子里人一走就斷檔了。還有一個(gè)容易被忽略的維度是代碼的生命周期。如果項(xiàng)目是短期一次性交付審查流程可以適當(dāng)簡(jiǎn)化如果是長(zhǎng)期維護(hù)的核心系統(tǒng)那審查記錄的積累價(jià)值會(huì)隨時(shí)間指數(shù)級(jí)上升。我參與過(guò)一個(gè)維護(hù)了三年多的項(xiàng)目后期新人排查問(wèn)題時(shí)歷史審查記錄成了最重要的參考資料之一很多“為什么當(dāng)初這么寫(xiě)”的疑問(wèn)都能在里面找到答案。3. 核心環(huán)節(jié)拆解與實(shí)操要點(diǎn)3.1 審查規(guī)則的公開(kāi)化落地規(guī)則公開(kāi)不是把一份文檔扔到共享目錄里就完事了那樣沒(méi)人會(huì)看。我的做法是把規(guī)則拆成可執(zhí)行的檢查項(xiàng)嵌入到日常工具鏈里。具體來(lái)說(shuō)分三步走。第一步梳理出團(tuán)隊(duì)最在意的五到八條核心規(guī)則。注意不要貪多規(guī)則太多等于沒(méi)有規(guī)則。比如“所有對(duì)外接口必須有參數(shù)校驗(yàn)”“數(shù)據(jù)庫(kù)操作必須考慮事務(wù)邊界”“新增依賴(lài)必須說(shuō)明理由”這些都是高頻且容易出問(wèn)題的點(diǎn)。把這些規(guī)則寫(xiě)成簡(jiǎn)短的檢查清單放在合并請(qǐng)求的模板里每次創(chuàng)建合并請(qǐng)求時(shí)自動(dòng)帶出來(lái)。第二步給每條規(guī)則配一個(gè)正例和一個(gè)反例。光說(shuō)“要有參數(shù)校驗(yàn)”太抽象直接貼兩段代碼對(duì)比一段是沒(méi)校驗(yàn)導(dǎo)致空指針的一段是加了校驗(yàn)正常運(yùn)行的。這種對(duì)比比任何文字描述都直觀。我通常會(huì)把正反例放在團(tuán)隊(duì)內(nèi)部的代碼規(guī)范文檔里審查時(shí)如果發(fā)現(xiàn)類(lèi)似問(wèn)題直接甩鏈接過(guò)去省去重復(fù)解釋。第三步定期回顧規(guī)則的執(zhí)行情況。每個(gè)月花半小時(shí)看看哪些規(guī)則被頻繁違反是規(guī)則本身不合理還是大家沒(méi)注意到。如果是規(guī)則不合理就改規(guī)則如果是沒(méi)注意到就在團(tuán)隊(duì)會(huì)議上強(qiáng)調(diào)一下。這個(gè)回顧機(jī)制很重要它讓規(guī)則保持“活”的狀態(tài)而不是變成一份沒(méi)人看的死文檔。注意規(guī)則公開(kāi)的目的是減少重復(fù)溝通不是制造審批障礙。如果一條規(guī)則導(dǎo)致大量合并請(qǐng)求被卡住首先要反思規(guī)則本身是否過(guò)于嚴(yán)苛而不是去指責(zé)審查者或開(kāi)發(fā)者。3.2 審查意見(jiàn)的寫(xiě)法與分級(jí)審查意見(jiàn)怎么寫(xiě)直接決定了審查效率。我見(jiàn)過(guò)太多“這個(gè)寫(xiě)法不好”之類(lèi)的模糊意見(jiàn)被審查者看完一頭霧水來(lái)回追問(wèn)好幾輪才能搞清楚問(wèn)題在哪。高效的審查意見(jiàn)應(yīng)該包含三個(gè)要素指出具體位置、說(shuō)明問(wèn)題是什么、給出改進(jìn)方向。舉個(gè)例子與其說(shuō)“這里性能有問(wèn)題”不如說(shuō)“第 45 行的循環(huán)里每次都查一次數(shù)據(jù)庫(kù)數(shù)據(jù)量大的時(shí)候會(huì)有 N1 查詢(xún)問(wèn)題建議改成批量查詢(xún)后傳入”。前者是評(píng)價(jià)后者是信息。審查意見(jiàn)的價(jià)值在于傳遞信息而不是表達(dá)態(tài)度。另外我習(xí)慣把審查意見(jiàn)分成三個(gè)級(jí)別用不同的前綴標(biāo)注方便被審查者快速判斷優(yōu)先級(jí)。級(jí)別前綴含義處理要求阻塞級(jí)[必須]存在正確性、安全性或嚴(yán)重性能問(wèn)題必須修改后才能合并建議級(jí)[建議]有更好的寫(xiě)法但不影響當(dāng)前功能可改可不改鼓勵(lì)改討論級(jí)[討論]涉及設(shè)計(jì)方向需要團(tuán)隊(duì)達(dá)成共識(shí)先討論清楚再?zèng)Q定這個(gè)分級(jí)的好處是被審查者一眼就能看出哪些是必須處理的哪些是可以后續(xù)優(yōu)化的。沒(méi)有分級(jí)的話(huà)所有意見(jiàn)看起來(lái)都一樣重被審查者要么全部照改導(dǎo)致效率低下要么全部忽略導(dǎo)致問(wèn)題遺漏。3.3 審查過(guò)程的透明化記錄透明化記錄的核心是讓審查過(guò)程本身成為可檢索的資產(chǎn)。具體操作上我要求團(tuán)隊(duì)做到兩點(diǎn)第一所有審查討論必須在合并請(qǐng)求的評(píng)論區(qū)進(jìn)行不允許轉(zhuǎn)到私聊第二每條審查意見(jiàn)在解決后由提出者或解決者標(biāo)記“已解決”并簡(jiǎn)要說(shuō)明解決方式。第一點(diǎn)的意義在于私聊里的討論是“暗知識(shí)”只有參與的人知道評(píng)論區(qū)里的討論是“明知識(shí)”任何人后續(xù)都能看到。我遇到過(guò)好幾次新人遇到一個(gè)類(lèi)似問(wèn)題搜一下歷史合并請(qǐng)求就找到了解決方案省去了重新踩坑的時(shí)間。第二點(diǎn)的意義在于形成閉環(huán)。如果一條意見(jiàn)提了但沒(méi)人標(biāo)記解決過(guò)幾天就沒(méi)人記得它到底處理了沒(méi)有。標(biāo)記“已解決”并說(shuō)明解決方式既是對(duì)提出者的反饋也是給后續(xù)查閱者的交代。我通常建議解決方式寫(xiě)一句話(huà)就行比如“已改為批量查詢(xún)”或“經(jīng)討論保持現(xiàn)狀原因是當(dāng)前數(shù)據(jù)量下性能可接受”。提示透明化記錄剛開(kāi)始推行時(shí)會(huì)有阻力很多人覺(jué)得“寫(xiě)這些太麻煩”。我的經(jīng)驗(yàn)是前兩周強(qiáng)制要求第三周開(kāi)始大家就會(huì)感受到好處——尤其是當(dāng)有人通過(guò)搜索歷史記錄快速解決了一個(gè)問(wèn)題時(shí)這種正向反饋會(huì)推動(dòng)習(xí)慣自然形成。4. 完整實(shí)操流程與關(guān)鍵步驟實(shí)現(xiàn)4.1 從創(chuàng)建合并請(qǐng)求到合并的完整鏈路一套完整的open-code-review流程從開(kāi)發(fā)者提交代碼到最終合并通常經(jīng)過(guò)六個(gè)環(huán)節(jié)。我把每個(gè)環(huán)節(jié)的關(guān)鍵動(dòng)作和注意事項(xiàng)拆開(kāi)來(lái)講。環(huán)節(jié)一開(kāi)發(fā)者自檢。在創(chuàng)建合并請(qǐng)求之前開(kāi)發(fā)者先對(duì)照檢查清單過(guò)一遍自己的代碼。這一步能過(guò)濾掉大量低級(jí)問(wèn)題比如格式錯(cuò)誤、遺漏的調(diào)試代碼、未更新的注釋。我通常建議開(kāi)發(fā)者自檢時(shí)重點(diǎn)看三樣?xùn)|西改動(dòng)是否只包含本次任務(wù)相關(guān)的內(nèi)容、是否有明顯的邏輯漏洞、是否補(bǔ)充了必要的測(cè)試。自檢做得好后續(xù)審查效率能提升至少三成。環(huán)節(jié)二創(chuàng)建合并請(qǐng)求并填寫(xiě)描述。合并請(qǐng)求的描述不是走過(guò)場(chǎng)它直接影響審查者能否快速理解改動(dòng)意圖。一個(gè)好的描述包含四部分本次改動(dòng)解決了什么問(wèn)題、采用了什么方案、有哪些地方需要審查者特別關(guān)注、有沒(méi)有已知的遺留問(wèn)題。我見(jiàn)過(guò)很多合并請(qǐng)求描述只寫(xiě)一句“修復(fù) bug”審查者看完完全不知道背景只能自己去猜效率極低。環(huán)節(jié)三自動(dòng)化檢查觸發(fā)。合并請(qǐng)求創(chuàng)建后自動(dòng)觸發(fā)格式檢查、靜態(tài)分析、單元測(cè)試等流水線。這一步的結(jié)果會(huì)直接顯示在合并請(qǐng)求頁(yè)面上審查者一眼就能看到哪些檢查通過(guò)了、哪些失敗了。我的建議是自動(dòng)化檢查不通過(guò)時(shí)不允許進(jìn)入人工審查環(huán)節(jié)。否則審查者花時(shí)間看完代碼最后發(fā)現(xiàn)測(cè)試都沒(méi)過(guò)純屬浪費(fèi)。環(huán)節(jié)四人工審查。審查者收到通知后在規(guī)定時(shí)間內(nèi)完成審查。這里有個(gè)實(shí)操細(xì)節(jié)審查者應(yīng)該先看整體改動(dòng)范圍再看具體代碼。先看改了哪些文件、每個(gè)文件改了多少行對(duì)改動(dòng)規(guī)模有個(gè)預(yù)期再逐文件細(xì)看。如果一上來(lái)就逐行看很容易在細(xì)節(jié)里迷失忽略了整體設(shè)計(jì)上的問(wèn)題。環(huán)節(jié)五意見(jiàn)回復(fù)與修改。開(kāi)發(fā)者收到審查意見(jiàn)后逐條回復(fù)。同意的就修改并標(biāo)記已解決不同意的就說(shuō)明理由并進(jìn)入討論。這個(gè)環(huán)節(jié)最忌諱的是“沉默修改”——改了但不回復(fù)審查者不知道你改了沒(méi)有、改得對(duì)不對(duì)。我要求團(tuán)隊(duì)里每條意見(jiàn)都必須有回復(fù)哪怕只是“已修改”三個(gè)字。環(huán)節(jié)六最終確認(rèn)與合并。所有阻塞級(jí)意見(jiàn)解決、自動(dòng)化檢查通過(guò)后由審查者或指定的合并負(fù)責(zé)人做最終確認(rèn)。確認(rèn)時(shí)快速掃一遍改動(dòng)和意見(jiàn)處理情況沒(méi)問(wèn)題就合并。合并后合并請(qǐng)求本身作為歷史記錄保留供后續(xù)查閱。4.2 審查時(shí)間的分配與節(jié)奏控制審查時(shí)間怎么分配是一個(gè)容易被忽視但影響巨大的問(wèn)題。我見(jiàn)過(guò)兩種極端一種是審查者拖到截止日期前才看結(jié)果匆匆掃一眼就通過(guò)了另一種是審查者過(guò)于投入一個(gè)合并請(qǐng)求看了一個(gè)多小時(shí)提了三十多條意見(jiàn)其中一半是吹毛求疵。我的經(jīng)驗(yàn)是單個(gè)合并請(qǐng)求的人工審查時(shí)間控制在 15 到 30 分鐘之間比較合理。超過(guò) 30 分鐘要么是改動(dòng)太大需要拆分要么是審查者陷入了細(xì)節(jié)。低于 15 分鐘除非改動(dòng)確實(shí)很小否則很可能看得不夠仔細(xì)。節(jié)奏控制上我建議團(tuán)隊(duì)約定一個(gè)審查響應(yīng)時(shí)間比如“合并請(qǐng)求創(chuàng)建后 4 小時(shí)內(nèi)必須有初審反饋”。這個(gè)時(shí)間不是要求 4 小時(shí)內(nèi)審?fù)甓且?4 小時(shí)內(nèi)讓開(kāi)發(fā)者知道“有人開(kāi)始看了”或者“今天排不開(kāi)明天上午看”。這個(gè)反饋本身就能減少開(kāi)發(fā)者的焦慮和等待成本。另外我強(qiáng)烈建議每天固定一個(gè)時(shí)間段集中處理審查比如上午十點(diǎn)到十一點(diǎn)。集中處理比隨時(shí)打斷工作效率高得多因?yàn)閷彶樾枰M(jìn)入一種“深度閱讀”的狀態(tài)頻繁切換上下文會(huì)讓審查質(zhì)量大打折扣。4.3 審查意見(jiàn)的跟蹤與閉環(huán)意見(jiàn)跟蹤是很多團(tuán)隊(duì)做得最差的一環(huán)。常見(jiàn)的問(wèn)題是審查者提了意見(jiàn)開(kāi)發(fā)者改了但審查者沒(méi)回頭看最后合并了也不知道意見(jiàn)到底解決沒(méi)有。解決這個(gè)問(wèn)題我推薦一個(gè)簡(jiǎn)單的做法用合并請(qǐng)求平臺(tái)自帶的“重新請(qǐng)求審查”功能。具體流程是開(kāi)發(fā)者修改完代碼后點(diǎn)擊“重新請(qǐng)求審查”審查者收到通知后重點(diǎn)看之前提過(guò)意見(jiàn)的地方是否已解決以及修改是否引入了新問(wèn)題。這個(gè)動(dòng)作把“提意見(jiàn)”和“確認(rèn)解決”串成了一個(gè)閉環(huán)避免了意見(jiàn)懸空。如果團(tuán)隊(duì)用的平臺(tái)沒(méi)有這個(gè)功能那就退而求其次在評(píng)論區(qū)手動(dòng) 審查者并附上一句“已按意見(jiàn)修改請(qǐng)確認(rèn)”。雖然麻煩一點(diǎn)但效果是一樣的。關(guān)鍵是不能讓意見(jiàn)在沒(méi)有確認(rèn)的情況下被合并否則審查就失去了意義。注意閉環(huán)不等于每條意見(jiàn)都必須被采納。如果開(kāi)發(fā)者有充分理由不采納某條建議審查者認(rèn)可后標(biāo)記“已討論保持現(xiàn)狀”也算閉環(huán)。閉環(huán)的核心是“有結(jié)論”而不是“必須改”。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 審查流于形式的三種典型表現(xiàn)審查流于形式是推行open-code-review過(guò)程中最常見(jiàn)的挑戰(zhàn)。我總結(jié)下來(lái)有三種典型表現(xiàn)每種背后的原因和應(yīng)對(duì)方式都不一樣。表現(xiàn)一審查意見(jiàn)全是“LGTM”看起來(lái)不錯(cuò)。這種情況通常發(fā)生在審查者對(duì)被審查的代碼領(lǐng)域不熟悉或者審查者本身比較忙、沒(méi)時(shí)間細(xì)看。應(yīng)對(duì)方式是輪換審查者不要讓同一個(gè)人總是審查同一個(gè)模塊。另外可以在團(tuán)隊(duì)里培養(yǎng)“交叉審查”的習(xí)慣讓后端看前端、前端看后端雖然領(lǐng)域不熟但往往能發(fā)現(xiàn)一些“當(dāng)局者迷”的問(wèn)題。表現(xiàn)二審查意見(jiàn)全是格式問(wèn)題。這說(shuō)明自動(dòng)化檢查沒(méi)做到位把本該機(jī)器做的事情推給了人工。應(yīng)對(duì)方式是加強(qiáng)自動(dòng)化檢查把格式、命名規(guī)范、導(dǎo)入順序這些能自動(dòng)化的全部自動(dòng)化人工審查只關(guān)注邏輯和設(shè)計(jì)。表現(xiàn)三審查意見(jiàn)提了但沒(méi)人改。這通常是流程閉環(huán)沒(méi)做好或者團(tuán)隊(duì)文化里不重視審查意見(jiàn)。應(yīng)對(duì)方式是明確合并門(mén)檻規(guī)定“有未解決的阻塞級(jí)意見(jiàn)時(shí)不允許合并”并在工具層面做限制。文化問(wèn)題需要時(shí)間但工具層面的硬約束能先兜住底線。5.2 審查者與被審查者的溝通摩擦審查過(guò)程中出現(xiàn)溝通摩擦很多時(shí)候不是因?yàn)榧夹g(shù)分歧而是因?yàn)楸磉_(dá)方式。我見(jiàn)過(guò)審查者寫(xiě)“這個(gè)寫(xiě)法很糟糕”被審查者看完直接炸毛后面討論全變成了情緒對(duì)抗。也見(jiàn)過(guò)被審查者回復(fù)“你不懂這個(gè)業(yè)務(wù)”審查者覺(jué)得被冒犯從此不再認(rèn)真審查。避免摩擦我的經(jīng)驗(yàn)是把握三個(gè)原則。第一對(duì)事不對(duì)人。說(shuō)“這段代碼在并發(fā)場(chǎng)景下會(huì)有問(wèn)題”而不是“你怎么連這個(gè)都沒(méi)考慮”。第二先問(wèn)再斷??吹讲焕斫獾膶?xiě)法先問(wèn)“這里為什么這么寫(xiě)”而不是直接說(shuō)“這里寫(xiě)錯(cuò)了”。很多時(shí)候被審查者有特殊考慮問(wèn)一句就能避免誤會(huì)。第三承認(rèn)自己的局限。審查者不可能什么都懂遇到不熟悉的領(lǐng)域坦誠(chéng)說(shuō)“這塊我不太熟建議找某某也看一下”比硬著頭皮提意見(jiàn)更負(fù)責(zé)任。5.3 常見(jiàn)問(wèn)題速查表問(wèn)題現(xiàn)象可能原因排查方向解決建議合并請(qǐng)求積壓嚴(yán)重審查者太少或?qū)彶闀r(shí)間不固定統(tǒng)計(jì)每個(gè)審查者的待審數(shù)量增加審查者、固定每日審查時(shí)段審查意見(jiàn)反復(fù)討論無(wú)結(jié)論缺乏決策機(jī)制看討論是否涉及設(shè)計(jì)方向引入“討論級(jí)”意見(jiàn)約定超時(shí)由負(fù)責(zé)人拍板自動(dòng)化檢查頻繁誤報(bào)規(guī)則配置過(guò)嚴(yán)或工具版本問(wèn)題查看誤報(bào)的具體規(guī)則調(diào)整規(guī)則閾值或升級(jí)工具版本新人不敢提審查意見(jiàn)團(tuán)隊(duì)氛圍過(guò)于層級(jí)化觀察新人是否只回復(fù)不主動(dòng)提明確“審查意見(jiàn)不分資歷”鼓勵(lì)新人從提問(wèn)開(kāi)始審查記錄難以檢索意見(jiàn)散落在不同平臺(tái)檢查是否所有討論都在合并請(qǐng)求內(nèi)統(tǒng)一到倉(cāng)庫(kù)平臺(tái)禁止私聊討論5.4 幾個(gè)我踩過(guò)的坑第一個(gè)坑是過(guò)早追求全面自動(dòng)化。剛開(kāi)始搭建流程時(shí)我想把所有能自動(dòng)化的都自動(dòng)化結(jié)果配置了一堆工具維護(hù)成本極高反而拖慢了整體效率。后來(lái)我調(diào)整策略先手動(dòng)跑通流程確認(rèn)哪些環(huán)節(jié)確實(shí)高頻且重復(fù)再逐步自動(dòng)化。這個(gè)順序很重要先有流程再有工具而不是反過(guò)來(lái)。第二個(gè)坑是審查規(guī)則一刀切。早期我要求所有合并請(qǐng)求都必須有兩個(gè)審查者通過(guò)結(jié)果小改動(dòng)也要等兩個(gè)人效率很低。后來(lái)改成按改動(dòng)規(guī)模分級(jí)小改動(dòng)比如改個(gè)文案、修個(gè)明顯 bug一個(gè)人審就行大改動(dòng)涉及核心邏輯或架構(gòu)調(diào)整才需要兩個(gè)人。這個(gè)分級(jí)讓審查資源用在了刀刃上。第三個(gè)坑是忽視審查者的成長(zhǎng)。審查能力不是天生的新人審查者一開(kāi)始可能只會(huì)看格式問(wèn)題需要有人帶。我的做法是讓新人先跟著資深審查者一起審看資深審查者怎么提意見(jiàn)、怎么把握尺度幾次之后新人就能獨(dú)立審查了。這個(gè)“傳幫帶”的過(guò)程不能省否則審查者梯隊(duì)會(huì)斷檔。6. 工具鏈選型與集成建議6.1 代碼托管平臺(tái)自帶審查功能的取舍大多數(shù)代碼托管平臺(tái)都自帶合并請(qǐng)求和審查功能這是最省事的起點(diǎn)。自帶的優(yōu)勢(shì)是零集成成本審查記錄和代碼天然綁定不需要額外維護(hù)。劣勢(shì)是定制能力有限比如你想加一個(gè)“審查意見(jiàn)必須包含改進(jìn)建議”的強(qiáng)制校驗(yàn)自帶功能可能做不到。我的建議是團(tuán)隊(duì)規(guī)模在三十人以?xún)?nèi)優(yōu)先用平臺(tái)自帶功能。把精力花在流程設(shè)計(jì)和規(guī)則落地上而不是工具開(kāi)發(fā)上。等流程跑順了、確實(shí)遇到自帶功能解決不了的問(wèn)題再考慮自建或引入第三方工具。過(guò)早自建工具往往做出來(lái)的東西還不如自帶的好用。6.2 自動(dòng)化檢查工具的集成方式自動(dòng)化檢查工具的選擇核心看兩點(diǎn)檢查速度和誤報(bào)率。檢查速度太慢會(huì)拖慢整個(gè)流程誤報(bào)率太高會(huì)讓開(kāi)發(fā)者逐漸忽略檢查結(jié)果。我通常建議從最基礎(chǔ)的三樣開(kāi)始格式檢查、靜態(tài)分析、單元測(cè)試。這三樣覆蓋了大部分低級(jí)問(wèn)題而且成熟工具很多配置成本低。集成方式上推薦用流水線觸發(fā)的方式而不是本地鉤子。本地鉤子的問(wèn)題是開(kāi)發(fā)者可以繞過(guò)而且不同開(kāi)發(fā)者的本地環(huán)境可能不一致。流水線觸發(fā)保證了檢查環(huán)境的一致性而且結(jié)果直接顯示在合并請(qǐng)求頁(yè)面上審查者一眼就能看到。提示自動(dòng)化檢查的規(guī)則不要一次配太多先配最核心的幾條跑一段時(shí)間后再根據(jù)實(shí)際情況增加。規(guī)則太多會(huì)導(dǎo)致檢查時(shí)間變長(zhǎng)開(kāi)發(fā)者等待成本上升反而降低效率。6.3 審查數(shù)據(jù)的統(tǒng)計(jì)與復(fù)盤(pán)審查數(shù)據(jù)能反映很多流程問(wèn)題但統(tǒng)計(jì)口徑要選對(duì)。我通常關(guān)注四個(gè)指標(biāo)平均審查時(shí)長(zhǎng)、平均意見(jiàn)數(shù)量、意見(jiàn)解決率、合并請(qǐng)求平均大小。平均審查時(shí)長(zhǎng)反映響應(yīng)速度如果持續(xù)超過(guò)一天說(shuō)明審查資源不足或流程有瓶頸。平均意見(jiàn)數(shù)量反映審查深度如果長(zhǎng)期低于一條說(shuō)明審查可能流于形式。意見(jiàn)解決率反映閉環(huán)情況如果低于九成說(shuō)明有意見(jiàn)被忽略了。合并請(qǐng)求平均大小反映拆分習(xí)慣如果持續(xù)超過(guò)六百行說(shuō)明開(kāi)發(fā)者沒(méi)有養(yǎng)成小步提交的習(xí)慣。這些數(shù)據(jù)不需要天天看每月復(fù)盤(pán)一次就夠了。復(fù)盤(pán)時(shí)重點(diǎn)看趨勢(shì)變化而不是絕對(duì)值。比如這個(gè)月平均審查時(shí)長(zhǎng)比上個(gè)月多了半天就要想想是哪里變慢了。數(shù)據(jù)是輔助判斷的工具不是考核的標(biāo)尺這一點(diǎn)要跟團(tuán)隊(duì)說(shuō)清楚否則大家會(huì)為了數(shù)據(jù)好看而做表面功夫。7. 從單團(tuán)隊(duì)到跨團(tuán)隊(duì)協(xié)作的擴(kuò)展思路7.1 跨團(tuán)隊(duì)審查的挑戰(zhàn)與應(yīng)對(duì)當(dāng)一個(gè)項(xiàng)目涉及多個(gè)團(tuán)隊(duì)協(xié)作時(shí)open-code-review的復(fù)雜度會(huì)明顯上升。最大的挑戰(zhàn)是上下文不一致A 團(tuán)隊(duì)的代碼規(guī)范可能和 B 團(tuán)隊(duì)不一樣A 團(tuán)隊(duì)覺(jué)得理所當(dāng)然的寫(xiě)法B 團(tuán)隊(duì)可能完全看不懂。應(yīng)對(duì)這個(gè)挑戰(zhàn)我的經(jīng)驗(yàn)是先統(tǒng)一底線再保留差異。底線是指那些所有團(tuán)隊(duì)都必須遵守的規(guī)則比如安全規(guī)范、接口約定、日志格式。這些底線規(guī)則由跨團(tuán)隊(duì)的技術(shù)負(fù)責(zé)人共同制定寫(xiě)進(jìn)每個(gè)團(tuán)隊(duì)的檢查清單里。差異是指各團(tuán)隊(duì)內(nèi)部的風(fēng)格偏好比如目錄結(jié)構(gòu)、命名習(xí)慣這些不強(qiáng)制統(tǒng)一但要求每個(gè)團(tuán)隊(duì)在自己的代碼范圍內(nèi)保持一致。另一個(gè)挑戰(zhàn)是審查責(zé)任歸屬??鐖F(tuán)隊(duì)改動(dòng)由誰(shuí)審查我的做法是改動(dòng)涉及哪個(gè)團(tuán)隊(duì)的代碼就由哪個(gè)團(tuán)隊(duì)負(fù)責(zé)審查。如果改動(dòng)同時(shí)涉及多個(gè)團(tuán)隊(duì)就由發(fā)起方團(tuán)隊(duì)先審再請(qǐng)受影響團(tuán)隊(duì)確認(rèn)。這個(gè)規(guī)則簡(jiǎn)單清晰避免了“三個(gè)和尚沒(méi)水喝”的推諉。7.2 審查規(guī)范的版本化管理當(dāng)團(tuán)隊(duì)數(shù)量增多、規(guī)范逐漸細(xì)化時(shí)審查規(guī)范本身也需要版本化管理。我見(jiàn)過(guò)一些團(tuán)隊(duì)把規(guī)范寫(xiě)在文檔里改了之后沒(méi)人知道導(dǎo)致不同時(shí)期合并的代碼遵循的是不同版本的規(guī)范。我的做法是把審查規(guī)范放在代碼倉(cāng)庫(kù)里和代碼一起版本管理。每次修改規(guī)范都走一次合并請(qǐng)求這樣規(guī)范的變更歷史清晰可查而且修改規(guī)范本身也經(jīng)過(guò)了審查。這個(gè)做法還有一個(gè)好處新人入職時(shí)直接看規(guī)范的合并請(qǐng)求歷史就能了解團(tuán)隊(duì)在審查標(biāo)準(zhǔn)上的演進(jìn)過(guò)程比看一份靜態(tài)文檔收獲大得多。7.3 審查文化的長(zhǎng)期建設(shè)工具和流程能解決大部分問(wèn)題但最終決定open-code-review成敗的是文化。我觀察下來(lái)審查文化好的團(tuán)隊(duì)有幾個(gè)共同特征審查者愿意花時(shí)間、被審查者愿意聽(tīng)意見(jiàn)、討論對(duì)事不對(duì)人、意見(jiàn)有分歧時(shí)能理性決策。建設(shè)這種文化沒(méi)有捷徑靠的是一點(diǎn)一滴的積累。我的經(jīng)驗(yàn)是從正面案例入手當(dāng)某個(gè)合并請(qǐng)求通過(guò)審查發(fā)現(xiàn)了一個(gè)隱藏很深的問(wèn)題時(shí)在團(tuán)隊(duì)里公開(kāi)表?yè)P(yáng)一下當(dāng)某次討論雖然激烈但最終達(dá)成了共識(shí)時(shí)也提一句。這些正向反饋會(huì)慢慢塑造團(tuán)隊(duì)對(duì)審查的認(rèn)知——審查不是找茬而是共同把代碼質(zhì)量做好。反過(guò)來(lái)如果團(tuán)隊(duì)里出現(xiàn)了“審查就是走形式”的言論或者有人因?yàn)樘崃藢彶橐庖?jiàn)被嘲諷那就要及時(shí)干預(yù)。文化這東西好的時(shí)候能自我強(qiáng)化壞的時(shí)候也會(huì)自我強(qiáng)化關(guān)鍵看早期怎么引導(dǎo)。8. 一些實(shí)操中的個(gè)人體會(huì)我在多個(gè)團(tuán)隊(duì)推行過(guò)open-code-review這套思路最大的體會(huì)是流程的價(jià)值不在于流程本身而在于它強(qiáng)迫團(tuán)隊(duì)把隱性的質(zhì)量共識(shí)顯性化。很多團(tuán)隊(duì)其實(shí)對(duì)“什么是好代碼”有共識(shí)但這些共識(shí)停留在每個(gè)人腦子里沒(méi)有變成可傳遞、可執(zhí)行的規(guī)則。審查流程就是把這些共識(shí)挖出來(lái)、寫(xiě)下來(lái)、用起來(lái)的過(guò)程。另一個(gè)體會(huì)是不要追求一步到位。我見(jiàn)過(guò)一些團(tuán)隊(duì)一上來(lái)就設(shè)計(jì)了一套非常復(fù)雜的審查流程結(jié)果推行兩周就沒(méi)人執(zhí)行了。更好的做法是先跑一個(gè)最小可用的版本比如只做“合并請(qǐng)求必須有人審查”這一條跑順了再加規(guī)則、加自動(dòng)化、加統(tǒng)計(jì)。流程是長(zhǎng)出來(lái)的不是設(shè)計(jì)出來(lái)的。最后分享一個(gè)小技巧定期讓團(tuán)隊(duì)成員互相看對(duì)方的審查記錄。不是看代碼而是看審查意見(jiàn)怎么寫(xiě)的、怎么回復(fù)的。這個(gè)動(dòng)作能快速拉齊團(tuán)隊(duì)的審查水平比任何培訓(xùn)都有效。我試過(guò)幾次效果很好尤其是對(duì)新人審查者的成長(zhǎng)幫助很大。