放式代碼審查:讓橡皮圖章式審查真正失效的實(shí)戰(zhàn)方案)
大約半年前我們差點(diǎn)把一個(gè)嚴(yán)重缺陷帶上生產(chǎn)環(huán)境。改動(dòng)本身小得不能再小——某個(gè)配置項(xiàng)的超時(shí)時(shí)間從3秒改成10秒PR里只有幾行代碼當(dāng)天就被審查者點(diǎn)了通過(guò)。上線前一天一位剛?cè)肼毜耐略谡砥渌枨髸r(shí)偶然看到這個(gè)改動(dòng)隨口問(wèn)了一句“雙活切換的時(shí)候這個(gè)超時(shí)時(shí)間會(huì)影響什么”這一問(wèn)把所有人都問(wèn)住了。后來(lái)我們順著調(diào)用鏈排查才發(fā)現(xiàn)這個(gè)參數(shù)直接影響故障轉(zhuǎn)移的判定窗口。在特定場(chǎng)景下系統(tǒng)會(huì)多等近十秒才開(kāi)始切換這對(duì)一個(gè)追求秒級(jí)恢復(fù)的內(nèi)部系統(tǒng)來(lái)說(shuō)幾乎是不可接受的。這件事讓我第一次認(rèn)真思考代碼審查到底在審什么為什么那么多雙眼睛都看過(guò)卻沒(méi)有一個(gè)人發(fā)現(xiàn)這里有問(wèn)題帶著這個(gè)疑問(wèn)我花了兩個(gè)多月時(shí)間重新梳理團(tuán)隊(duì)的審查制度也系統(tǒng)研究了開(kāi)放式代碼審查open-code-review這條路線。今天這篇內(nèi)容不打算講空泛的概念。我想把這次轉(zhuǎn)變的真實(shí)過(guò)程、落地方案、踩過(guò)的坑和評(píng)估方法完整地分享出來(lái)。無(wú)論你是一個(gè)十幾人的小團(tuán)隊(duì)還是幾十人以上的中大型團(tuán)隊(duì)只要你發(fā)現(xiàn)自己團(tuán)隊(duì)里的代碼審查開(kāi)始流于形式這篇文章應(yīng)該能給你一些直接能用的思路。1. 差點(diǎn)上線的那個(gè)缺陷傳統(tǒng)代碼審查的失效現(xiàn)場(chǎng)1.1 一個(gè)改了超時(shí)時(shí)間的PR所有人都點(diǎn)了通過(guò)先說(shuō)回那個(gè)事故。那次改動(dòng)本身來(lái)自一個(gè)很常規(guī)的需求排查線上偶發(fā)超時(shí)問(wèn)題時(shí)負(fù)責(zé)的同事覺(jué)得“連接超時(shí)設(shè)得短一點(diǎn)太容易誤判”于是把某個(gè)內(nèi)部RPC調(diào)用的超時(shí)時(shí)間從3秒調(diào)成了10秒。從代碼語(yǔ)法層面看這就是一處配置修改類(lèi)型沒(méi)錯(cuò)、格式?jīng)]錯(cuò)、不影響編譯甚至README里的注釋也同步更新了。問(wèn)題在于這個(gè)配置不只是“單次調(diào)用超時(shí)”它還會(huì)參與一個(gè)更上層的判斷邏輯——節(jié)點(diǎn)健康檢查會(huì)在多個(gè)指標(biāo)里讀取這個(gè)超時(shí)值用來(lái)計(jì)算故障切換的等待窗口。改之前3秒故障判定很快改到10秒后主節(jié)點(diǎn)抖動(dòng)時(shí)從節(jié)點(diǎn)要等更久才會(huì)觸發(fā)切換。這個(gè)聯(lián)動(dòng)關(guān)系藏在三個(gè)不同目錄、四層調(diào)用之外光看這個(gè)PR的diff根本看不出來(lái)。更諷刺的是這個(gè)PR當(dāng)時(shí)有三個(gè)人review過(guò)一個(gè)看了格式一個(gè)看了注釋一個(gè)問(wèn)了“這個(gè)值寫(xiě)死會(huì)不會(huì)有其他地方引用”但沒(méi)有繼續(xù)深挖。三個(gè)人都點(diǎn)了通過(guò)其中兩個(gè)人留下了“LGTM”。這大概就是我們常說(shuō)的橡皮圖章審查——流程走完了質(zhì)量卻沒(méi)有被真正把關(guān)。1.2 橡皮圖章效應(yīng)背后的四個(gè)結(jié)構(gòu)性原因事后復(fù)盤(pán)時(shí)我一度以為是團(tuán)隊(duì)?wèi)B(tài)度問(wèn)題但越挖越發(fā)現(xiàn)這是結(jié)構(gòu)性的。我總結(jié)出四個(gè)原因幾乎在所有傳統(tǒng)審查流程里都存在。第一個(gè)原因信息不對(duì)稱太嚴(yán)重。審查者只能看到“發(fā)生了什么變化”卻看不到“為什么會(huì)有這次變化”。如果PR描述里只寫(xiě)著“調(diào)整超時(shí)時(shí)間”那審查者的所有判斷都只能靠猜。可現(xiàn)實(shí)中能寫(xiě)清楚背景、影響面、回滾方案的PR往往是極少數(shù)。第二個(gè)原因社會(huì)惰性在作祟。同一個(gè)組的同事抬頭不見(jiàn)低頭見(jiàn)誰(shuí)愿意每次都在PR里提一堆意見(jiàn)、顯得自己很懂尤其當(dāng)提議總是以“這個(gè)應(yīng)該改一下吧”的溫和口吻出現(xiàn)時(shí)很多實(shí)質(zhì)問(wèn)題會(huì)被禮貌地帶過(guò)。時(shí)間一長(zhǎng)審查就變成了一種社交禮儀。第三個(gè)原因反饋來(lái)得太晚。很多團(tuán)隊(duì)習(xí)慣在分支合并前才做審查甚至等功能全部完成、自測(cè)通過(guò)后才貼出PR。這時(shí)候提意見(jiàn)的代價(jià)已經(jīng)非常高了——改代碼、改測(cè)試、重新回歸每一步都意味著時(shí)間和情緒成本。于是大家不約而同地選擇了“能過(guò)就過(guò)”。第四個(gè)原因?qū)彶榉秶囊曇懊^(qū)。傳統(tǒng)reviewer通常只關(guān)注代碼的正確性很少有人站在測(cè)試、運(yùn)維、下游調(diào)用方的角度去思考??汕∏∈沁@些視角最容易發(fā)現(xiàn)“這個(gè)改動(dòng)會(huì)影響故障轉(zhuǎn)移窗口”這類(lèi)問(wèn)題。那次事故之后我沒(méi)有立刻去懲罰任何當(dāng)事人反而意識(shí)到一件事如果審查看不到背景、看不到影響、看不到全局那雙眼睛再多也是白搭。這也直接把我推向了open-code-review的方向。2. open-code-review 到底“開(kāi)放”了什么——從權(quán)限開(kāi)放到視角開(kāi)放2.1 讓不同角色帶著各自上下文進(jìn)入審查很多人第一次聽(tīng)到“開(kāi)放式代碼審查”第一反應(yīng)是“把代碼公開(kāi)給所有人看”。如果只是這樣理解那就大錯(cuò)特錯(cuò)了。open-code-review真正開(kāi)放的不是代碼的可見(jiàn)范圍而是審查者的視角范圍。傳統(tǒng)審查的參與者幾乎全是同組開(kāi)發(fā)人員背景高度同質(zhì)化思維方式也高度趨同。開(kāi)放式審查要做的事情是把測(cè)試、運(yùn)維、產(chǎn)品、下游系統(tǒng)維護(hù)者甚至剛?cè)肼毑痪眠€帶著“無(wú)知之幕”的新人邀請(qǐng)到審查環(huán)節(jié)中來(lái)。我舉一個(gè)真實(shí)發(fā)生過(guò)的例子。某次一個(gè)同事在改動(dòng)一個(gè)錯(cuò)誤碼枚舉類(lèi)看起來(lái)只是新增了一個(gè)值不影響已有字段代碼編譯也通過(guò)。按傳統(tǒng)流程這個(gè)PR很快就能合入。但在開(kāi)放式審查中測(cè)試團(tuán)隊(duì)的一位同學(xué)提出了一個(gè)疑問(wèn)“我們有一條自動(dòng)化用例會(huì)精確匹配錯(cuò)誤碼列表的數(shù)量這個(gè)改動(dòng)會(huì)不會(huì)讓用例失敗”一查果然會(huì)。這個(gè)影響范圍寫(xiě)代碼的人不知道開(kāi)發(fā)視角的reviewer也不容易發(fā)現(xiàn)但測(cè)試同學(xué)一眼就看到。這就是開(kāi)放式審查的核心價(jià)值讓帶著不同上下文的人替你在意你沒(méi)想到的維度。測(cè)試想的是回歸范圍運(yùn)維想的是發(fā)布和回滾下游調(diào)用方想的是兼容性新人想的是“這段代碼我能不能讀懂”。這些視角疊在一起才會(huì)讓一個(gè)缺陷卡在合入之前。2.2 審查對(duì)象從“變更代碼”擴(kuò)展到“變更意圖”傳統(tǒng)審查只盯著diff但diff只能告訴你“改了什么”無(wú)法告訴你“為什么這么改”和“這樣改的代價(jià)是什么”。開(kāi)放式審查要求PR必須攜帶完整的變更意圖讓審查者不用靠猜就能理解作者的決策過(guò)程。從那次超時(shí)事故之后我給團(tuán)隊(duì)定了一條硬規(guī)矩任何PR的描述里必須回答三個(gè)問(wèn)題——這次改動(dòng)要解決什么問(wèn)題為什么不選其他方案如果改動(dòng)出錯(cuò)最壞影響是什么如果連這三個(gè)問(wèn)題都寫(xiě)不明白那這個(gè)PR就不允許進(jìn)入審查環(huán)節(jié)直接打回去補(bǔ)充背景。不要小看這條規(guī)則。很多人在寫(xiě)PR描述的時(shí)候被迫重新思考了一遍自己的改動(dòng)。有人寫(xiě)著寫(xiě)著發(fā)現(xiàn)“其實(shí)還有另一個(gè)方案更穩(wěn)妥”有人發(fā)現(xiàn)自己根本沒(méi)想清楚改動(dòng)的影響范圍。這個(gè)過(guò)程本身就是一種質(zhì)量過(guò)濾。而審查者拿到背景信息后問(wèn)的問(wèn)題也從“這里為什么不寫(xiě)注釋”變成了“這個(gè)判斷邏輯在異常場(chǎng)景里會(huì)不會(huì)誤觸發(fā)”提問(wèn)質(zhì)量完全不一樣。這就是把審查對(duì)象從“代碼”拓展到“意圖”的威力。代碼是靜態(tài)的意圖才是動(dòng)態(tài)的。審查真正要做的是對(duì)齊決策過(guò)程和風(fēng)險(xiǎn)邊界而不是單純找語(yǔ)法毛病。2.3 劃定合理的開(kāi)放邊界而不是無(wú)限擴(kuò)散“開(kāi)放”不等于“失控”。剛開(kāi)始推行開(kāi)放式審查時(shí)我犯過(guò)一個(gè)錯(cuò)誤把所有PR都推到同一個(gè)群請(qǐng)大家“有空就看看”。結(jié)果是人多嘴雜討論被淹沒(méi)在大量無(wú)關(guān)評(píng)論里真正有價(jià)值的意見(jiàn)反而沒(méi)人看到。后來(lái)我們重新定義了開(kāi)放范圍規(guī)則其實(shí)很簡(jiǎn)單。核心原則有三條。第一高風(fēng)險(xiǎn)變更必須開(kāi)放。凡是涉及配置項(xiàng)、對(duì)外接口、數(shù)據(jù)庫(kù)結(jié)構(gòu)、消息協(xié)議、安全策略的改動(dòng)無(wú)論改動(dòng)量多小都必須邀請(qǐng)跨角色審查者參與。第二低風(fēng)險(xiǎn)變更盡量開(kāi)放但不必強(qiáng)制。比如注釋修正、格式化、純文檔改動(dòng)可以直接合入不必占用大家注意力。第三每個(gè)PR必須有一個(gè)“主審查人”其他參與者是“輔助審查人”。主審查人負(fù)責(zé)匯總意見(jiàn)、推進(jìn)修改、最終拍板輔助審查人的意見(jiàn)再重要也不需要通過(guò)群里來(lái)回吵架來(lái)解決。在這個(gè)邊界下“開(kāi)放”變成了一種有組織的資源分配而不是一場(chǎng)無(wú)序的圍觀。審查質(zhì)量提升的同時(shí)審查效率沒(méi)有斷崖式下跌這是后來(lái)團(tuán)隊(duì)能長(zhǎng)期堅(jiān)持下來(lái)的關(guān)鍵。3. 把開(kāi)放式審查落進(jìn)日常一套可復(fù)制的實(shí)操流程3.1 第一步建立審查清單拒絕無(wú)規(guī)則開(kāi)放如果開(kāi)放式審查只給了一堆自由度而沒(méi)有任何約束結(jié)果一定是一團(tuán)亂麻。所以我們的第一步是先建立一份適合團(tuán)隊(duì)現(xiàn)狀的審查清單。這份清單不追求大而全只盯著最容易被漏掉、且代價(jià)最高的風(fēng)險(xiǎn)點(diǎn)。我建議你們第一步可以參考這份模板再根據(jù)自己的技術(shù)棧裁減[ ] 變更描述是否說(shuō)明了“解決什么問(wèn)題”和“不這樣做的后果”[ ] 是否存在對(duì)外接口、錯(cuò)誤碼、消息格式、數(shù)據(jù)庫(kù)字段的改動(dòng)如果有是否考慮了下游兼容性[ ] 配置項(xiàng)變更是否被其他邏輯引用引用方在異常情況下會(huì)怎樣表現(xiàn)[ ] 新增分支或判斷邏輯是否覆蓋了空值、超時(shí)、并發(fā)沖突、冪等場(chǎng)景[ ] 是否有對(duì)應(yīng)的日志和監(jiān)控指標(biāo)異常時(shí)能不能快速定位問(wèn)題[ ] 測(cè)試用例是否覆蓋了核心路徑和至少一個(gè)邊界場(chǎng)景[ ] 回滾方案是什么如果新代碼出問(wèn)題能否在十分鐘內(nèi)恢復(fù)這套清單的核心理念是強(qiáng)迫作者在發(fā)起審查之前自己先扮演一遍“多角色審查者”。很多問(wèn)題會(huì)在這一關(guān)被消滅真正走到審查環(huán)節(jié)的PR質(zhì)量會(huì)有明顯提高。3.2 第二步異步為主、同步兜底的節(jié)奏設(shè)計(jì)開(kāi)放式審查最大的困難是參與者的時(shí)間不同步。如果每次討論都要求所有人同時(shí)在線那這份流程一定推行不下去。我們的做法是異步審查作為主渠道同步評(píng)審只作為兜底手段。異步審查的工具并不神秘一個(gè)支持評(píng)論線程的PR/MR頁(yè)面就夠了。關(guān)鍵在節(jié)奏設(shè)定提交PR后24小時(shí)內(nèi)所有輔助審查者必須給出首批反饋72小時(shí)內(nèi)主審查人要完成第一輪總結(jié)所有阻塞性意見(jiàn)解決后才允許合入。這里有個(gè)容易被忽略的細(xì)節(jié)異步評(píng)論必須按“阻塞級(jí)”和“建議級(jí)”標(biāo)記。阻塞級(jí)意見(jiàn)P0會(huì)阻止合入必須經(jīng)過(guò)修改或明確解釋后解決建議級(jí)意見(jiàn)P1可以討論但允許作者在說(shuō)明理由后不采納。如果沒(méi)有這層分級(jí)任何一個(gè)評(píng)論都可能被當(dāng)成必須修改的強(qiáng)制要求那開(kāi)放反而變成了一種形式主義——因?yàn)槊總€(gè)人都怕?lián)?zé)任于是每個(gè)意見(jiàn)都要求改。同步評(píng)審會(huì)我們只在兩類(lèi)場(chǎng)景強(qiáng)制召開(kāi)。一類(lèi)是高風(fēng)險(xiǎn)的跨模塊變更比如消息協(xié)議調(diào)整或數(shù)據(jù)庫(kù)遷移另一類(lèi)是異步討論超過(guò)三輪還沒(méi)有結(jié)論的爭(zhēng)議。這種會(huì)議通??刂圃?0分鐘以內(nèi)只解決分歧不重讀代碼會(huì)前必須把相關(guān)評(píng)論鏈接貼到會(huì)議說(shuō)明里。跑過(guò)幾次之后你會(huì)發(fā)現(xiàn)大多數(shù)PR根本不需要走到同步會(huì)議這一步。3.3 第三步反饋閉環(huán)與爭(zhēng)議仲裁機(jī)制開(kāi)放式審查最怕一件事討論得熱火朝天最后作者偷偷改了代碼、悄悄合入討論區(qū)成了擺設(shè)。所以一定要建立反饋閉環(huán)讓每一個(gè)重要評(píng)論都落到一個(gè)明確結(jié)果上。我們的做法是強(qiáng)制要求作者在合入前寫(xiě)一段“審查結(jié)論”。不是簡(jiǎn)單地說(shuō)“已修改”而是逐條列明哪些意見(jiàn)已采納、修改了哪里哪些意見(jiàn)未采納、理由是什么。這個(gè)步驟看起來(lái)額外增加了作者的工作量但長(zhǎng)期看反而節(jié)省了溝通成本——因?yàn)楹罄m(xù)如果有人翻出這段歷史不需要重頭爭(zhēng)論一遍。爭(zhēng)議還有一個(gè)解決通道。當(dāng)某個(gè)意見(jiàn)雙方各執(zhí)一詞時(shí)主審查人可以引入第三個(gè)中立角色來(lái)仲裁。這個(gè)角色只對(duì)“風(fēng)險(xiǎn)大小”負(fù)責(zé)不對(duì)“代碼審美”負(fù)責(zé)。審美問(wèn)題可以擱置風(fēng)險(xiǎn)問(wèn)題必須閉環(huán)。有一個(gè)小技巧很值得分享把常見(jiàn)爭(zhēng)議沉淀成團(tuán)隊(duì)的“設(shè)計(jì)決策記錄”。比如“為什么錯(cuò)誤碼不允許復(fù)用”“為什么這個(gè)配置項(xiàng)必須走發(fā)布系統(tǒng)而不是直接改數(shù)據(jù)庫(kù)”把這些決策的來(lái)龍去脈寫(xiě)清楚后續(xù)再討論同類(lèi)問(wèn)題時(shí)直接把鏈接甩過(guò)去就行。這套記錄隨著時(shí)間積累會(huì)變成團(tuán)隊(duì)里最值錢(qián)的文檔之一。4. 開(kāi)放性帶來(lái)的三個(gè)坑以及我的應(yīng)對(duì)方式4.1 人海戰(zhàn)術(shù)的噪音用“主審查人意見(jiàn)分級(jí)”消化開(kāi)放式審查推行一個(gè)月后我遇到最明顯的問(wèn)題是噪音。一個(gè)PR可能有七八個(gè)人參與評(píng)論有人從代碼風(fēng)格角度說(shuō)“這個(gè)函數(shù)應(yīng)該拆開(kāi)”有人從性能角度說(shuō)“這里可以加個(gè)緩存”有人甚至只是進(jìn)來(lái)點(diǎn)了個(gè)贊。這些評(píng)論本身沒(méi)有惡意但匯聚起來(lái)會(huì)讓作者產(chǎn)生一種“到處都要改”的壓迫感反而不知道該優(yōu)先處理什么。后來(lái)我明確了兩條鐵律。第一每個(gè)PR只設(shè)置一位主審查人其他所有人都是參與審查人。主審查人的職責(zé)不是寫(xiě)最多評(píng)論而是給所有評(píng)論做排序和裁決。第二所有意見(jiàn)必須按P0/P1標(biāo)記。P0是必須改P1是可以討論沒(méi)有第三種模糊狀態(tài)。這兩條規(guī)則落地之后噪音問(wèn)題大幅緩解。有效的評(píng)論被置頂、被跟蹤無(wú)效的討論可以快速收尾。“人多”不再意味著“意見(jiàn)多而散”而是意味著“視角多且有序”。4.2 跨團(tuán)隊(duì)語(yǔ)境鴻溝用背景文檔和問(wèn)題標(biāo)注來(lái)彌合開(kāi)放式審查引入跨團(tuán)隊(duì)參與者后語(yǔ)境鴻溝是繞不開(kāi)的問(wèn)題。下游系統(tǒng)的維護(hù)者看到一段代碼可能完全不了解你們的領(lǐng)域術(shù)語(yǔ)提出的問(wèn)題看起來(lái)會(huì)“很基礎(chǔ)”有時(shí)甚至?xí)l(fā)作者的反感“這不是常識(shí)嗎”但認(rèn)真想一想如果對(duì)方在共享上下文嚴(yán)重不足的情況下依然能提出一個(gè)合理疑問(wèn)那恰恰說(shuō)明這段代碼的表達(dá)還不夠清晰。后來(lái)我們把這種“基礎(chǔ)問(wèn)題”當(dāng)成一種免費(fèi)的用戶反饋凡是有人需要額外解釋的地方大概率都值得補(bǔ)充注釋或文檔。我們也做了一些制度上的彌補(bǔ)。跨團(tuán)隊(duì)審查時(shí)作者必須隨PR附上簡(jiǎn)短背景說(shuō)明或指向已有的設(shè)計(jì)文檔審查者在提問(wèn)時(shí)要區(qū)分“詢問(wèn)”和“建議”兩類(lèi)表述。詢問(wèn)是“我想了解為什么這么設(shè)計(jì)”建議是“我認(rèn)為應(yīng)該改成那樣”。這兩種表述的對(duì)抗性完全不同前者給作者留了解釋的余地后者容易引起防御心理。讓這兩者分開(kāi)討論氛圍會(huì)顯著改善。4.3 流程失控風(fēng)險(xiǎn)用角色細(xì)分和路由規(guī)則守住節(jié)奏“開(kāi)放”推行到一定程度另一個(gè)風(fēng)險(xiǎn)開(kāi)始冒頭流程失控。有人會(huì)覺(jué)得所有PR都該被所有人review一遍也有人會(huì)覺(jué)得既然已經(jīng)開(kāi)放了那每一個(gè)人都有責(zé)任攔住任何問(wèn)題。結(jié)果就是審查范圍無(wú)限膨脹合入速度越來(lái)越慢團(tuán)隊(duì)開(kāi)始疲憊甚至有人開(kāi)始想辦法繞過(guò)流程。必須承認(rèn)開(kāi)放式審查的目標(biāo)不是“讓所有人為所有東西負(fù)責(zé)”而是“讓最合適的人在他們最擅長(zhǎng)的維度上負(fù)責(zé)”。所以我們引入了明確的路由規(guī)則根據(jù)變更類(lèi)型自動(dòng)指定需要參與的審查角色。比如接口變更默認(rèn)路由給下游維護(hù)者和測(cè)試負(fù)責(zé)人數(shù)據(jù)庫(kù)變更默認(rèn)路由給運(yùn)維負(fù)責(zé)人和性能負(fù)責(zé)人純前端樣式改動(dòng)只路由給前端主審查人不強(qiáng)制邀請(qǐng)后端同事。路由規(guī)則不是死的任何人仍然可以主動(dòng)關(guān)注感興趣的PR但默認(rèn)渠道保證了核心角色一定會(huì)被覆蓋不會(huì)被海量通知淹沒(méi)。這一步做完之后開(kāi)放式審查才真正從“熱鬧的圍觀”變成了“高效的協(xié)作”。5. 怎么判斷這套辦法真的有效——指標(biāo)與跡象5.1 四個(gè)可以量化的審查指標(biāo)推行了兩三個(gè)月后我開(kāi)始思考一個(gè)問(wèn)題怎么證明開(kāi)放式的方向是對(duì)的除了“感覺(jué)氛圍變好了”還需要一些更實(shí)在的判斷依據(jù)。我建議從四個(gè)指標(biāo)入手。第一個(gè)是缺陷泄漏率也就是上線后才發(fā)現(xiàn)的問(wèn)題占提交總數(shù)的比例。這是最直觀的結(jié)果指標(biāo)。開(kāi)放式審查生效后很多缺陷會(huì)被擋在合入前泄漏率應(yīng)該呈下降趨勢(shì)。第二個(gè)是審查覆蓋率即經(jīng)歷過(guò)非作者之外至少一名角色審查的PR比例。這個(gè)指標(biāo)要結(jié)合“有效審查”來(lái)看單純統(tǒng)計(jì)點(diǎn)了通過(guò)的PR沒(méi)有意義。我們的定義是“至少有一條實(shí)質(zhì)性評(píng)論且這條評(píng)論被作者采納或明確回應(yīng)”才計(jì)為一次有效審查。第三個(gè)是反饋?lái)憫?yīng)時(shí)間即從PR提交到第一條有效評(píng)論之間的間隔。理想狀態(tài)是核心角色在24小時(shí)內(nèi)給出反饋。如果這個(gè)時(shí)間過(guò)長(zhǎng)說(shuō)明路由規(guī)則或人員配置需要調(diào)整。第四個(gè)是評(píng)論采納率即被采納的評(píng)論數(shù)除以總有效評(píng)論數(shù)。如果采納率偏低有可能是審查者在提無(wú)關(guān)痛癢的意見(jiàn)也可能是作者在防御性拒絕正確建議。無(wú)論哪種情況都值得深入看一眼。這四組數(shù)據(jù)不需要很復(fù)雜的統(tǒng)計(jì)工具讓主審查人在每周復(fù)盤(pán)時(shí)手工記錄一遍就夠了??吹枚嗔粟厔?shì)自然會(huì)浮現(xiàn)出來(lái)。5.2 文化層面的三個(gè)觀察點(diǎn)比指標(biāo)更難量化、但也更重要的是團(tuán)隊(duì)文化層面有沒(méi)有真正發(fā)生變化。我特別喜歡用三個(gè)問(wèn)題來(lái)觀察。第一個(gè)問(wèn)題新人敢不敢在PR里向資深同事提意見(jiàn)如果敢說(shuō)明團(tuán)隊(duì)的安全感是真實(shí)的。第二個(gè)問(wèn)題做方案討論時(shí)有沒(méi)有人愿意主動(dòng)說(shuō)“這個(gè)方案我不同意因?yàn)轱L(fēng)險(xiǎn)在哪兒”而不是等別人先說(shuō)。第三個(gè)問(wèn)題代碼出問(wèn)題后大家的第一反應(yīng)是“誰(shuí)寫(xiě)的鍋”還是“流程哪里漏了防”如果是后者說(shuō)明審查的文化已經(jīng)超越了追責(zé)的層面。我們團(tuán)隊(duì)里發(fā)生過(guò)一件讓我印象很深的事。某個(gè)配置項(xiàng)在發(fā)布時(shí)漏掉了灰度策略雖然沒(méi)造成用戶影響但事后復(fù)盤(pán)時(shí)負(fù)責(zé)方案的同學(xué)自己提出來(lái)“當(dāng)時(shí)如果有個(gè)人問(wèn)我一句灰度怎么做的我就不會(huì)直接上全量了”。這種自我驅(qū)動(dòng)式的暴露問(wèn)題比任何指標(biāo)都更能說(shuō)明審查文化開(kāi)始真正扎根了。我不認(rèn)為開(kāi)放式審查是代碼質(zhì)量的銀彈它更像是一套刻意設(shè)計(jì)的“出錯(cuò)機(jī)制”——讓錯(cuò)誤在成本最低的地方盡早暴露。而它要成立的前提是每個(gè)人都先放下“這是我的代碼”的領(lǐng)地意識(shí)愿意把代碼看作團(tuán)隊(duì)的共同作品。這不是一個(gè)技術(shù)問(wèn)題至少不全是。如果你也想在自己的團(tuán)隊(duì)嘗試我的建議非常具體從最近一個(gè)讓你睡不著的風(fēng)險(xiǎn)變更開(kāi)始。把它開(kāi)放給測(cè)試、運(yùn)維、下游維護(hù)者要求作者寫(xiě)清楚變更意圖給每條評(píng)論分級(jí)開(kāi)一場(chǎng)不超過(guò)30分鐘的同步評(píng)審會(huì)。跑完這一次你應(yīng)該就能感受到那層窗戶紙到底有多薄。后面的事情自然就有動(dòng)力做了。