制與最佳實(shí)踐)
我做了六年多的一線研發(fā)也帶過(guò)十幾個(gè)人的技術(shù)團(tuán)隊(duì)被問(wèn)得最多的一個(gè)問(wèn)題不是“怎么把代碼寫得更好”而是“code review到底怎么做才不流于形式”。open-code-review 這個(gè)詞我最早是在一個(gè)開源社區(qū)的技術(shù)分享里看到的它指向的并不是某一個(gè)具體的工具而是一整套把代碼審查過(guò)程開放化、標(biāo)準(zhǔn)化、可度量的工程實(shí)踐。今天我就把這個(gè)主題掰開揉碎從為什么、是什么、怎么落到最常見的坑完整聊一遍。這個(gè)問(wèn)題為什么值得專門寫一篇長(zhǎng)文因?yàn)槲乙娺^(guò)太多團(tuán)隊(duì)把 code review 做成了一件讓所有人痛苦的事情要么是提交代碼后石沉大海沒(méi)人看要么是 reviewer 只回一句 “LGTM” 就算完事要么是每次 review 都變成大型吵架現(xiàn)場(chǎng)。這些現(xiàn)象的本質(zhì)其實(shí)是團(tuán)隊(duì)沒(méi)有建立起一套合理的、透明的、有反饋閉環(huán)的審查機(jī)制。open-code-review 要解決的正是這個(gè)問(wèn)題。這篇文章適合誰(shuí)看如果你是工程師想搞清楚怎么提交更容易被 review、怎么給同事提意見不傷人如果你是技術(shù) Leader 或項(xiàng)目負(fù)責(zé)人想在公司內(nèi)部推行或者改良代碼審查制度又或者你只是對(duì)研發(fā)流程建設(shè)感興趣想了解一個(gè)成熟團(tuán)隊(duì)是怎么把“代碼質(zhì)量”這件事從口號(hào)變成機(jī)制的。這篇文章都能給你一套可以落地執(zhí)行的思路和模板。1. 代碼審查的本質(zhì)與核心價(jià)值1.1 代碼審查到底在審什么很多人對(duì) code review 的理解還停留在“找 bug”這個(gè)層面。這個(gè)理解不能說(shuō)是錯(cuò)的但它遠(yuǎn)遠(yuǎn)不夠。我個(gè)人的定義是代碼審查是在代碼合并到主干之前由其他工程師對(duì)變更進(jìn)行的系統(tǒng)性檢查它的對(duì)象不僅僅是“這段代碼能不能跑”還包括“這段代碼應(yīng)不應(yīng)該這么寫”“這樣寫未來(lái)會(huì)不會(huì)給別人挖坑”“這個(gè)方案是否貼合當(dāng)前項(xiàng)目的架構(gòu)方向”。代碼審查能發(fā)現(xiàn)的問(wèn)題遠(yuǎn)不止邏輯錯(cuò)誤。舉個(gè)例子有一次我們的支付模塊接入了一個(gè)新的第三方渠道提交的代碼本身邏輯是對(duì)的單測(cè)也過(guò)了但 review 的時(shí)候另一個(gè)同事發(fā)現(xiàn)這里的回調(diào)處理用了同步阻塞的方式在高并發(fā)場(chǎng)景下會(huì)把線程池打滿。這種問(wèn)題寫代碼的人自己很難發(fā)現(xiàn)因?yàn)樗X子里全是“怎么把流程跑通”而審查者站在旁觀者角度更容易看到性能隱患和邊界條件。所以代碼審查本質(zhì)上是一次“多人協(xié)作的質(zhì)量兜底”。它不是要證明寫代碼的人水平不行而是要借助團(tuán)隊(duì)所有人的經(jīng)驗(yàn)把個(gè)人的盲區(qū)補(bǔ)上。這個(gè)視角一旦擺正很多流程上的爭(zhēng)執(zhí)都會(huì)消失。1.2 開放式審查與傳統(tǒng)審查的差異傳統(tǒng)的 code review 往往是什么樣的我經(jīng)歷過(guò)很多團(tuán)隊(duì)最常見的模式是“提交-等待-確認(rèn)”三步走。開發(fā)者在分支上寫完代碼發(fā)一個(gè) PR 或 MR然后在群里喊一嗓子“求 review”然后就是漫長(zhǎng)的等待。運(yùn)氣好半小時(shí)有人理你運(yùn)氣不好第二天還掛在 pending 狀態(tài)。這種模式的問(wèn)題在于審查過(guò)程是一個(gè)黑盒參與者看不到彼此的節(jié)奏也沒(méi)有明確的優(yōu)先級(jí)最后代碼就積壓了。open-code-review 強(qiáng)調(diào)的“開放”我認(rèn)為包含三個(gè)維度過(guò)程透明、標(biāo)準(zhǔn)公開、反饋沉淀。過(guò)程透明是指每一次審查的狀態(tài)、進(jìn)度、阻塞點(diǎn)都是可見的。是等 reviewer還是 reviewer 提了問(wèn)題在等作者回復(fù)一目了然。標(biāo)準(zhǔn)公開是說(shuō)“什么樣的代碼能通過(guò)、什么樣的不能通過(guò)”不是某個(gè)人的口頭標(biāo)準(zhǔn)而是沉淀在團(tuán)隊(duì)審查清單里的共識(shí)。反饋沉淀是指 review 中產(chǎn)生的高質(zhì)量討論不應(yīng)該隨著 PR 關(guān)閉就消失它們應(yīng)該變成團(tuán)隊(duì)的知識(shí)資產(chǎn)下一次遇到類似問(wèn)題可以直接檢索參考。這三個(gè)維度看起來(lái)簡(jiǎn)單但真正能同時(shí)做到的團(tuán)隊(duì)其實(shí)不多。大部分團(tuán)隊(duì)能做好第一個(gè)維度就不錯(cuò)了。2. 搭建設(shè)計(jì)open-code-review 的核心機(jī)制與流程2.1 角色定義與責(zé)任邊界任何流程要跑起來(lái)首先得定義清楚誰(shuí)來(lái)干什么事。一個(gè)高效的開放審查體系里我習(xí)慣把參與者分成四個(gè)角色作者Author變更的提出者負(fù)責(zé)把需求拆成合理的提交提供必要的上下文說(shuō)明。審查者Reviewer對(duì)變更進(jìn)行評(píng)審的人對(duì)代碼的正確性、可維護(hù)性、安全性負(fù)責(zé)。維護(hù)者M(jìn)aintainer擁有合并權(quán)限的人在審查意見全部解決后做最終合并。小團(tuán)隊(duì)里這個(gè)角色往往由技術(shù)負(fù)責(zé)人或資深工程師兼任。旁聽者Observer對(duì)這個(gè)變更感興趣、愿意了解但不需要阻塞合并的人。角色分清楚了一個(gè)很重要的問(wèn)題也跟著浮出水面審查者到底該對(duì)什么負(fù)責(zé)我看過(guò)很多團(tuán)隊(duì)的 review 制度寫到“審查者需要對(duì)變更的質(zhì)量負(fù)全責(zé)”這句話聽著很提氣實(shí)際上會(huì)把人嚇跑。正確的設(shè)計(jì)是作者對(duì)代碼質(zhì)量負(fù)首要責(zé)任審查者這是提供獨(dú)立的專業(yè)意見維護(hù)者負(fù)責(zé)確認(rèn)意見是否被妥善處理。三方各司其職才能讓流程持續(xù)運(yùn)轉(zhuǎn)下去。2.2 小型團(tuán)隊(duì)的輕量流程設(shè)計(jì)如果你在的是一個(gè)不到十個(gè)人的小型團(tuán)隊(duì)上來(lái)就搭一套復(fù)雜的審查系統(tǒng)大概率會(huì)被抵制。我推薦的方式是用最小可用流程先跑起來(lái)分支策略用 GitHub Flow 或 GitLab Flow所有改動(dòng)通過(guò) Pull Request 合入主干每個(gè) PR 至少一個(gè) Reviewer所有評(píng)論必須被解決或明確回復(fù)后才能合并。這個(gè)流程里的關(guān)鍵不是規(guī)則的多與少而是沒(méi)有例外。很多團(tuán)隊(duì)一開始也定了規(guī)矩但遇到線上緊急修復(fù)就破例“先合再說(shuō)”到后來(lái)所有 PR 都走了這個(gè)例外通道。我自己的經(jīng)驗(yàn)是就算是緊急修復(fù)也要走 PR只是可以縮短審查時(shí)間、降低審查范圍——比如只審查改動(dòng)文件和安全審計(jì)而不是從頭到尾完整看一遍。這個(gè)習(xí)慣堅(jiān)持下來(lái)制度的嚴(yán)肅性就保住了。2.3 大規(guī)模團(tuán)隊(duì)的異步協(xié)作機(jī)制團(tuán)隊(duì)規(guī)模到三五十人以上的時(shí)候再用“每個(gè) PR 拉兩個(gè)人看一眼”的模式就會(huì)出問(wèn)題Reviewer 被 PR 淹沒(méi)該重點(diǎn)看的代碼反而被匆匆掃過(guò)。這時(shí)候需要引入異步協(xié)作機(jī)制。核心是“上下文前置”。作者在創(chuàng)建 PR 時(shí)必須把變更背景、設(shè)計(jì)方案、影響范圍、測(cè)試結(jié)果都寫清楚。Reviewer 拿到 PR 后不需要去找作者當(dāng)面聊才能看懂直接看描述就能進(jìn)入狀態(tài)。其次是“路由規(guī)則”不能隨機(jī)分配 reviewer而是根據(jù)代碼目錄、服務(wù)歸屬、技術(shù)棧標(biāo)簽自動(dòng)推薦該領(lǐng)域的負(fù)責(zé)人。最后是“層級(jí)升級(jí)”如果變更涉及核心模塊或資金安全必須額外指定一名資深審查者做二次確認(rèn)。異步協(xié)作不等于不溝通。真正高效的團(tuán)隊(duì)會(huì)把“評(píng)論里的討論”作為一等公民要求所有結(jié)論都能在 PR 頁(yè)面上留痕。有人在微信里說(shuō)“這個(gè)邏輯我覺得有問(wèn)題”不算數(shù)必須沉淀成評(píng)論讓所有參與者可見。文字溝通看似慢實(shí)際上是節(jié)省所有人的時(shí)間。3. 實(shí)操指南從零落地一套 open-code-review3.1 工具選型與基礎(chǔ)配置開門見山目前市面上的代碼托管平臺(tái)無(wú)論是 GitHub、GitLab 還是 Gitee內(nèi)置的 Pull Request / Merge Request 功能已經(jīng)足夠支撐一套完整的開放審查體系。絕大多數(shù)團(tuán)隊(duì)不需要額外搭建 Gerrit 或者 Review Board 那種重型工具除非你的場(chǎng)景是做嵌入式或芯片底層開發(fā)需要強(qiáng)制的 linear history 和嚴(yán)格的逐 commit 審查。以最常見的 GitHub 和 GitLab 為例你需要配置這些基礎(chǔ)項(xiàng)分支保護(hù)規(guī)則主分支不允許直接推送必須通過(guò) MR/PR 合入。最少審查人數(shù)一般設(shè)置為 1核心模塊可以設(shè) 2。審查通過(guò)后才能合并這個(gè)必須開啟否則規(guī)則形同虛設(shè)。過(guò)時(shí)審查自動(dòng)駁回如果新提交推上來(lái)舊 approval 自動(dòng)失效。合并類型根據(jù)團(tuán)隊(duì)偏好選 Merge Commit 或 Squash小團(tuán)隊(duì)我推薦 Squash歷史干凈。這些配置看起來(lái)瑣碎但每一條都是在堵住“偷懶的出口”。沒(méi)有分支保護(hù)就有人為了省事直接 push沒(méi)有過(guò)時(shí)審查駁回就有人刷了一個(gè) approval 然后瘋狂改代碼最后一個(gè)不 support 的 commit 也照樣并進(jìn)去。規(guī)則不是限制人的它是把最基礎(chǔ)的質(zhì)量底線工具化。3.2 可復(fù)用的代碼審查清單沒(méi)有 check list 的 review 就是跟著感覺走。感覺這個(gè)東西因人而異有人關(guān)注性能有人關(guān)注命名有人只關(guān)心能不能跑。要讓團(tuán)隊(duì)整體水平穩(wěn)定必須把檢查項(xiàng)標(biāo)準(zhǔn)化。我團(tuán)隊(duì)目前使用的清單分為五個(gè)維度每個(gè)維度的要點(diǎn)如下維度核心檢查點(diǎn)示例問(wèn)題正確性邏輯、邊界、異常處理、并發(fā)安全如果入?yún)⑹?null 會(huì)怎樣并發(fā)訪問(wèn)下這個(gè) Map 安全嗎架構(gòu)分層、耦合、職責(zé)、擴(kuò)展性這段新邏輯放在 Controller 層合理嗎性能復(fù)雜度、連接數(shù)、SQL、緩存這個(gè)查詢會(huì)走索引嗎會(huì)不會(huì)產(chǎn)生 N1 問(wèn)題可維護(hù)性命名、注釋、可讀性、重復(fù)代碼變量名能表達(dá)業(yè)務(wù)含義嗎這段邏輯與 3 個(gè)月前的 XX 模塊重復(fù)了安全性注入、越權(quán)、敏感信息、認(rèn)證授權(quán)這個(gè)接口做了權(quán)限校驗(yàn)嗎請(qǐng)求參數(shù)會(huì)被拼進(jìn) SQL 嗎這個(gè)清單不是一次 review 就要全部過(guò)一遍而是讓審查者在心里有一個(gè)掃描順序。我個(gè)人的習(xí)慣是先看整體設(shè)計(jì)再看核心邏輯正確性之后掃一遍資源使用最后看命名和注釋。把最寶貴的時(shí)間花在架構(gòu)和正確性上不要在變量命名上跟同事死磕。3.3 結(jié)構(gòu)化的 PR 描述模板一個(gè)連背景都不講的 PRReviewer 打開之后要先花二十分鐘猜測(cè)你要干什么。這對(duì)審查者時(shí)間的浪費(fèi)是不可接受的。我們團(tuán)隊(duì)的 PR 模板是強(qiáng)制性的寫在倉(cāng)庫(kù)的 PULL_REQUEST_TEMPLATE 文件里## 背景 這個(gè)改動(dòng)要解決什么問(wèn)題請(qǐng)用 2-3 句話說(shuō)明業(yè)務(wù)場(chǎng)景。 ## 改動(dòng)方案 你選擇了什么方案實(shí)現(xiàn)大致的設(shè)計(jì)思路是什么 如果存在多個(gè)候選方案請(qǐng)簡(jiǎn)單說(shuō)明為什么選擇這個(gè)。 ## 影響范圍 涉及哪些模塊是否有接口變更、數(shù)據(jù)庫(kù)變更、配置變更 是否有兼容性風(fēng)險(xiǎn) ## 測(cè)試驗(yàn)證 你做了哪些測(cè)試包括但不限于單測(cè)、集成測(cè)試、手工測(cè)試。 如果涉及界面改動(dòng)請(qǐng)附帶截圖。 ## 自檢清單 - [ ] 代碼通過(guò)本地測(cè)試 - [ ] 已補(bǔ)充或更新必要的單測(cè) - [ ] 已檢查敏感信息未提交密鑰、IP等 - [ ] 已更新相關(guān)文檔這個(gè)模板看起來(lái)長(zhǎng)但真正認(rèn)真填寫只需要十分鐘。它節(jié)省的是三五個(gè) Reviewer 每個(gè)人至少十分鐘的理解時(shí)間公式很清楚十分鐘換五十分鐘一定值得。3.4 寫審查意見的表達(dá)技巧代碼審查里最微妙的不是技術(shù)問(wèn)題而是人與人之間的溝通問(wèn)題。同一個(gè)意見換一種說(shuō)法接受度是完全不同的。我把我的心得總結(jié)成幾條硬規(guī)則第一對(duì)事不對(duì)人。評(píng)論里不要說(shuō)“你寫錯(cuò)了”要說(shuō)“這個(gè)邏輯我看起來(lái)有個(gè)隱患”。把人和問(wèn)題分開對(duì)方的防御心理會(huì)大幅度降低。第二區(qū)分“必須改”和“建議改”。我們團(tuán)隊(duì)用統(tǒng)一的標(biāo)簽來(lái)做這件事Nitpick 表示風(fēng)格小問(wèn)題Suggestion 表示改進(jìn)建議Blocking 表示必須修改才能合入。如果沒(méi)有這個(gè)區(qū)分作者會(huì)疲于應(yīng)對(duì)所有意見最后分不清輕重。第三給建議而不是只給批評(píng)。如果你覺得一個(gè)實(shí)現(xiàn)方式有問(wèn)題盡量給出替代方案。哪怕只是一個(gè)思路方向也比扔下一句“這里寫得不行”有價(jià)值得多。第四先肯定后建議。這不是客套而是技術(shù)事實(shí)一個(gè)變更里通常有 80% 的部分是可取的。直接指出有價(jià)值的部分再談需要改進(jìn)的部分溝通成本會(huì)顯著下降。4. 工具鏈進(jìn)階與自動(dòng)化輔助4.1 把靜態(tài)檢查接入審查流程人肉 review 的價(jià)值在于理解業(yè)務(wù)、把握設(shè)計(jì)、發(fā)現(xiàn)邏輯漏洞但人的精力是有限的如果每次都把時(shí)間花在“這里少了個(gè)空格”“這個(gè)變量名拼錯(cuò)了”上真正的設(shè)計(jì)問(wèn)題反而會(huì)被忽略。所以凡是機(jī)器能干的活一定要讓機(jī)器先干完。我建議在 CI 流水線里至少接入這幾個(gè)層級(jí)的自動(dòng)化工具基礎(chǔ)格式化校驗(yàn)如 Prettier、Spotless、代碼規(guī)范檢查如 ESLint、Checkstyle、靜態(tài)缺陷掃描如 SpotBugs、Semgrep。這些工具跑完后自動(dòng)把結(jié)果作為 PR 評(píng)論發(fā)出來(lái)審查者只需要看機(jī)器給出的結(jié)論里有沒(méi)有真正的誤判即可。靜態(tài)檢查的作用不僅是提升效率它還能把規(guī)則爭(zhēng)議從人跟人的討論中剝離出來(lái)。比如縮進(jìn)用空格還是 Tab、字符串用單引號(hào)還是雙引號(hào)這種問(wèn)題在機(jī)器接管之后就再也不用開會(huì)吵了。4.2 自動(dòng)化規(guī)則的建議清單在 GitHub Actions 或 GitLab CI 中我常用下面這些規(guī)則來(lái)輔助 review代碼格式檢查不通過(guò)直接 fail別讓審查者看格式問(wèn)題。影響文件數(shù)超過(guò)閾值比如 20 個(gè)時(shí)在 PR 中提示“是否考慮拆分為更小的變更”。新代碼覆蓋率低于項(xiàng)目標(biāo)準(zhǔn)時(shí)自動(dòng)標(biāo)記。依賴版本變更時(shí)自動(dòng)提醒是否有已知安全漏洞。禁止提交 .env、*.pem、id_rsa 等敏感文件用 pre-commit hook 攔截。這些自動(dòng)化規(guī)則的核心價(jià)值是讓審查者的精力聚焦在“人類才能判斷的問(wèn)題”上。我經(jīng)常跟團(tuán)隊(duì)說(shuō)的一句話是如果你的 code review 時(shí)間里有超過(guò)三分之一在看格式和記號(hào)問(wèn)題那不是你不行是你的工具鏈偷懶了。4.3 審查質(zhì)量數(shù)據(jù)化的經(jīng)驗(yàn)度量這個(gè)話題很敏感我不想把 review 做成 KPI 考核但完全不看數(shù)據(jù)你也不知道流程哪里出了問(wèn)題。我建議團(tuán)隊(duì)每個(gè)月看這幾個(gè)指標(biāo)僅用于診斷不用于績(jī)效平均首次響應(yīng)時(shí)間PR 創(chuàng)建到第一個(gè) reviewer 評(píng)論的時(shí)間衡量審查的及時(shí)性。平均審查周期PR 創(chuàng)建到合入的時(shí)間衡量整體效率。每個(gè) PR 的評(píng)論數(shù)太少說(shuō)明可能沒(méi)人認(rèn)真看太多說(shuō)明前置設(shè)計(jì)不夠充分。被回退的變更率合入后被 revert 的比例這個(gè)指標(biāo)能體現(xiàn)前置評(píng)審的有效性。把這些數(shù)據(jù)放到團(tuán)隊(duì)例會(huì)上討論的時(shí)候不要怪罪個(gè)人而是討論流程本身哪里可以優(yōu)化。比如“支付模塊的 PR 平均要兩天才能合入是因?yàn)橹挥欣贤跄軐徫覀兪遣皇且紤]做知識(shí)儲(chǔ)備、再培養(yǎng)一個(gè) reviewer”。這種對(duì)話是有建設(shè)性的。5. 團(tuán)隊(duì)落地中的常見問(wèn)題與實(shí)戰(zhàn)速查5.1 典型問(wèn)題與解決方案我在多個(gè)團(tuán)隊(duì)推過(guò)代碼審查機(jī)制幾乎每次都會(huì)遇到相似的問(wèn)題。挑幾個(gè)典型的來(lái)說(shuō)第一個(gè)典型問(wèn)題Reviewer 長(zhǎng)期不響應(yīng)。PR 掛在那邊三四天沒(méi)人看。這種問(wèn)題光靠催沒(méi)用要改機(jī)制。我現(xiàn)在的做法是超過(guò) 24 小時(shí)未響應(yīng)的 PR系統(tǒng)自動(dòng)升級(jí)提醒給團(tuán)隊(duì)負(fù)責(zé)人超過(guò) 48 小時(shí)升級(jí)給整個(gè)技術(shù)委員會(huì)。同時(shí)每個(gè)團(tuán)隊(duì)的 reviewer 池子不能只有一個(gè)人至少要保證每個(gè)模塊有兩個(gè)人能審否則這個(gè)人出差了整個(gè)團(tuán)隊(duì)的交付就被卡住了。第二個(gè)典型問(wèn)題PR 過(guò)大幾萬(wàn)個(gè)文件改成一片誰(shuí)都審不動(dòng)。這幾乎是大團(tuán)隊(duì)的標(biāo)配問(wèn)題。解決方式是在分支策略層面做硬性約束單個(gè) PR 變更文件數(shù)量超過(guò) 15 個(gè)、或新增行數(shù)超過(guò) 800 行時(shí)要求作者拆分。這個(gè)限制真的執(zhí)行起來(lái)之后Reviewer 的質(zhì)量反饋會(huì)明顯變好。第三個(gè)典型問(wèn)題審查意見被敷衍回一句“已修改”但實(shí)際沒(méi)改干凈。為了應(yīng)對(duì)這個(gè)問(wèn)題我們要求在提交新代碼時(shí)必須逐條回復(fù)評(píng)論狀態(tài)要么是“已修復(fù)見 x 行”要么是“不同意你的觀點(diǎn)原因是……”。不能只回復(fù)已修改要讓 reviewer 能快速定位驗(yàn)證。5.2 常見問(wèn)題速查表現(xiàn)象可能原因解決思路PR 長(zhǎng)期無(wú)人 reviewreviewer 池太小 / 沒(méi)有被提醒機(jī)制擴(kuò)大 reviewer 池設(shè)置 24h 升級(jí)提醒review 流于形式全是 LGTM團(tuán)隊(duì)沒(méi)有明確審查標(biāo)準(zhǔn)的共識(shí)引入審查清單明確各維度的最低要求改一行代碼也走完整 review 流程PR 范圍粒度過(guò)細(xì)定期合并小變更或者約定更高效的輕量流程review 變成吵架邊界角色不清討論摻雜個(gè)人情緒用書面化評(píng)論規(guī)范討論禁止人身評(píng)價(jià)聚焦問(wèn)題本身上線后仍出現(xiàn)線上 bugreview 沒(méi)攔住審查依賴人工缺少自動(dòng)化兜底補(bǔ)充單元測(cè)試、集成測(cè)試、灰度發(fā)布等質(zhì)量關(guān)卡5.3 真實(shí)案例復(fù)盤三次復(fù)盤帶來(lái)的節(jié)奏變化講一個(gè)印象挺深的例子。之前我們做一個(gè)訂單中臺(tái)的重構(gòu)團(tuán)隊(duì)里有幾個(gè)同事剛加入對(duì) review 流程不太適應(yīng)第一周的 PR 平均從創(chuàng)建到合入要 36 小時(shí)其中有 60% 的時(shí)間花在反復(fù)要求作者補(bǔ)充上下文上。我們后來(lái)做了一次復(fù)盤發(fā)現(xiàn)問(wèn)題的根因不是大家不認(rèn)真而是 PR 描述寫得太像“代碼作業(yè)”完全沒(méi)有背景信息。于是我們做了一個(gè)很簡(jiǎn)單的改動(dòng)PR 模板中把“背景”欄放在第一位并且明確要求寫清楚“如果不做這個(gè)改動(dòng)會(huì)發(fā)生什么”。就這一個(gè)變化第二周的首次響應(yīng)時(shí)間從 12 小時(shí)降到了 4 小時(shí)以內(nèi)因?yàn)?Reviewer 不用再花大量時(shí)間去猜作者的意圖了。后來(lái)我們還做了另一個(gè)調(diào)整把“單次審查代碼量”從拿整個(gè) PR 硬看改成要求按 commit 分批審查。很多人用 GitHub 只看 Files changed但一些不合理的中間狀態(tài)在最終文件里被掩蓋了。我們要求在分支內(nèi)以語(yǔ)義化 commit 為單位提交Reviewer 按 commit 順序逐步 review。一開始大家覺得麻煩但兩周之后所有人都感受到了好處出問(wèn)題時(shí)定位效率提升了非常多因?yàn)槊總€(gè) commit 是獨(dú)立可理解和可回滾的。6. 推行經(jīng)驗(yàn)與長(zhǎng)期維護(hù)6.1 如何在團(tuán)隊(duì)中逐步推行審查文化推行任何新流程最忌諱的就是“一刀切”強(qiáng)制上線。很多團(tuán)隊(duì)在引入代碼審查時(shí)犯的錯(cuò)就是今天宣布從明天開始所有 PR 必須兩個(gè) reviewer 通過(guò)才能合入沒(méi)有任何培訓(xùn)和緩沖期。結(jié)果就是全員抵觸流程名存實(shí)亡。我推薦“試點(diǎn)-反饋-推廣”的三步走。先選一個(gè)質(zhì)量意識(shí)比較好的小項(xiàng)目組做試點(diǎn)運(yùn)行兩周后收集反饋根據(jù)實(shí)際情況調(diào)整審查清單和流程細(xì)節(jié)再逐步擴(kuò)大到其他團(tuán)隊(duì)。在推行的過(guò)程中要不斷回答“這對(duì)我有什么好處”這個(gè)問(wèn)題。對(duì)工程師來(lái)說(shuō)最直接的好處是有人幫你兜底減少寫錯(cuò)代碼上線后被罵的概率對(duì)管理者來(lái)說(shuō)好處是代碼可維護(hù)性提升人員流動(dòng)時(shí)交接成本降低。另外還有一個(gè)訣竅在新人入職的前兩周專門安排一位資深工程師做“結(jié)對(duì) review”。不是讓他寫代碼而是讓他帶著新人完整走幾遍審查流程一邊看 PR 一邊講思路比如“先看全局再看細(xì)節(jié)”“這個(gè)文件的改動(dòng)要先看調(diào)用方”。這個(gè)方法比看十頁(yè)文檔都有效。6.2 審查文化的長(zhǎng)期維護(hù)制度能解決執(zhí)行力的問(wèn)題但解決不了持續(xù)性的問(wèn)題。很多團(tuán)隊(duì)的審查制度三個(gè)月之后就形同虛設(shè)了因?yàn)闆](méi)有人維護(hù)它。技術(shù)團(tuán)隊(duì)負(fù)責(zé)人要意識(shí)到代碼審查機(jī)制也需要像代碼一樣做重構(gòu)和維護(hù)。我目前的做法是每個(gè)季度做一次“流程回顧”把過(guò)去三個(gè)月的 PR 數(shù)據(jù)拉出來(lái)看看哪個(gè)模塊審查周期最長(zhǎng)、哪個(gè)環(huán)節(jié)出現(xiàn)大量返工、哪些類型的評(píng)論最常被忽略。根據(jù)這些數(shù)據(jù)調(diào)整審查清單的權(quán)重配置也可以更新自動(dòng)化檢查規(guī)則。比如我們發(fā)現(xiàn)數(shù)據(jù)層代碼的安全隱患在 review 中持續(xù)漏檢就把數(shù)據(jù)庫(kù)操作的靜態(tài)分析規(guī)則加進(jìn) CI并且在審查清單里增加了“SQL 注入與權(quán)限校驗(yàn)”專項(xiàng)檢查項(xiàng)。另外要建立“優(yōu)秀審查者”的示范效應(yīng)。每次團(tuán)隊(duì)分享會(huì)上可以讓大家投票選出最近一個(gè)月最有價(jià)值的 review 評(píng)論把這個(gè)評(píng)論拿出來(lái)逐字分析講清楚它為什么有價(jià)值、是發(fā)現(xiàn)了什么類型的問(wèn)題、給團(tuán)隊(duì)避免了多少損失。這個(gè)動(dòng)作能讓團(tuán)隊(duì)慢慢理解好的 review 不是找茬是用專業(yè)經(jīng)驗(yàn)幫別人避坑。在這個(gè)過(guò)程中我還有一個(gè)比較深的體會(huì)代碼審查機(jī)制的成熟度往往和團(tuán)隊(duì)的工程成熟度是正相關(guān)的。一個(gè)只能寫出“能跑的代碼”的團(tuán)隊(duì)跟一個(gè)追求“可維護(hù)的代碼”的團(tuán)隊(duì)對(duì) review 的態(tài)度是完全兩樣的。前者覺得 review 是阻礙后者覺得 review 是最后一道安全保障線。與其硬推制度不如先提升團(tuán)隊(duì)對(duì)工程質(zhì)量的整體認(rèn)知。最后再分享一個(gè)小技巧。如果你覺得團(tuán)隊(duì) review 的討論質(zhì)量還不夠高試試在 PR 描述里要求作者提交“自問(wèn)自答”這個(gè)改動(dòng)最大的風(fēng)險(xiǎn)可能是什么你在這個(gè)實(shí)現(xiàn)里最想被 review 的是哪個(gè)點(diǎn)有沒(méi)有你嘗試過(guò)但主動(dòng)放棄的替代方案就這三個(gè)問(wèn)題能讓審查者的注意力真正聚焦到高風(fēng)險(xiǎn)位置也能幫助作者養(yǎng)成“寫之前先想清楚”的習(xí)慣。我們團(tuán)隊(duì)用了半年明顯感覺整條鏈路上的思考深度上升了一個(gè)臺(tái)階。代碼審查這件事情門檻不高但是要把一套 open-code-review 機(jī)制真正跑起來(lái)并長(zhǎng)期運(yùn)轉(zhuǎn)需要的不只是工具配置更是一整個(gè)團(tuán)隊(duì)對(duì)質(zhì)量、對(duì)協(xié)作、對(duì)彼此時(shí)間價(jià)值的一致認(rèn)同。