代码评审这件事,干了这么多年开发,我早就想写篇东西好好聊聊了。团队里每次上线前最紧张的时刻,不是需求没做完,而是代码合并那一关——你永远不知道 review 的时候会被喷成什么样,也不知道自己要给别人挑出多少问题。这两年我带着团队把“open-code-review”这套机制彻底跑通了,“open”这个词我们理解成两层意思:一是工具和流程完全开放透明,评审记录对全组可见,不留死角;二是心态上开放,欢迎任何人对任何一段代码提问,不管你是刚来的实习生还是干了十年的老架构师。这套东西跑下来,上线事故率肉眼可见地降了,新人也成长得快了,今天我把整个从 0 到 1 的搭建过程和踩坑记录完整分享一下。
1. 内容整体设计与思路拆解
1.1 为什么要做开放式的代码评审
先说个真实的场景。以前我们组做 code review 基本是走过场:代码合并前拉上组长看一眼,或者干脆自己在群里丢一句“求 review”,大部分时候没人理,偶尔有人回一句“你看下这边逻辑对不对”,然后就没了。这种情况持续了快半年,结果就是线上事故不断,每次查问题都要翻半天代码,最后发现很多低级错误早就藏在代码里了,只是一直没人认真看。
我后来总结了一下,传统 review 为什么做不起来,核心就三个问题。第一个是责任不明确,代码写完了丢出去,没有人真正为“这段代码能安全上线”负责。第二个是评审动作太重太吓人,很多团队把 review 搞成了批斗会,年轻人根本不敢提意见,有想法也憋着。第三个是流程不可回溯,改了什么、谁看的、怎么看出来的问题,全都靠聊天记录,真出事了根本找不到依据。
open-code-review 的思路和传统 review 有一个本质区别:它把“评审”从个人自觉变成了一种团队机制。代码不是写给机器跑的,是写给人看的,这个道理很多书上都讲过,但真正把它落实到流程里的团队很少。我们做开放评审,就是要把代码拿出来公开晒、公开评、公开改,让问题在进入主干之前就被所有人看到。
1.2 打开“open”的三种具体方式
我们把“open”拆解成了三个可执行的动作,这也是整个机制的核心设计。
第一个是评审对象开放。不光是核心模块要 review,工具脚本、配置变更、测试用例、文档更新,全部纳入评审范围。因为经验告诉我们,很多线上事故不是坏在业务代码上,而是坏在配置改错、脚本写错、线上操作没留痕。有一次我们把缓存超时时间从 600 秒改成 60 秒,就一个小配置,没有人 review 直接就上了,结果数据库被打到报警。从那以后,所有变更一视同仁,全部走评审。
第二个是评审过程开放。每次评审的所有评论、所有修改、所有讨论,全部保留在仓库里,全组可见。不搞小窗口私聊,不搞线下拍板。这样做的核心价值在于沉淀:今天张三给李四提了一个边界条件的意见,这个讨论记录留下来,下个月王五写类似代码的时候,自己搜一下就能看到,不需要重复踩坑。
第三个是评审结果开放。任何一次评审的结论,不管是合入还是打回,都必须写清楚理由。合入的写明关键确认点,打回的写明必须修改和可选建议。这样做的好处是能减少“差不多就行了”的侥幸心理——每一条意见都有书面记录,下次再犯同样的错,谁也赖不掉。
1.3 这套机制解决的核心痛点
如果你问一个技术管理者,代码评审最大的痛点是什么,大概率不是“没人愿意看别人的代码”,而是“评审意见不痛不痒,提了也白提”。我们当初做 open-code-review 的出发点,就是要解决这个“形式主义评审”的问题。
第一个痛点是代码可读性差。很多开发者写代码的时候脑子里只有“跑通就行”,命名随意、逻辑堆叠、注释缺失。别人 review 的时候根本看不懂他的意图,自然提不出有价值的意见。开放评审倒逼每个开发者强制换位思考:我的代码会被人公开看,我就得把它写清楚,于是命名规范了,函数变小了,图省事的临时 hack 少了。
第二个痛点是“责任稀释”。以前代码上线出了问题,测试怪开发,开发怪产品,产品怪运维,最后谁都没责任。开放评审要求每一位合入代码的评审人署名,相当于代码头盔上刻着两个人的名字。写代码的人要对实现负责,评审的人要对把关负责,这个双重责任制一落地,双方立刻都认真起来。
第三个痛点是新人成长慢。刚到团队的新人,看老代码常常一头雾水,问人又不好意思。开放评审把全团队的技术讨论常态化之后,新人可以直接翻看任意一次历史评审记录,看老鸟们是怎么发现问题、怎么提意见、怎么协商修改的,这种学习素材比任何文档都真实。我们组有个应届生,入职三个月就靠翻评审记录把核心模块摸了个遍,成长速度快得离谱。
2. 工具选型与评审流程落地
2.1 开源工具选型对比与理由
open-code-review 搞起来,工具是第一步。市面上的代码托管和评审工具不少,我们重点对比了几种开源方案,简单说一下选型思路。
GitHub 的 Pull Request 是最常见的形态,优点是生态成熟、用户习惯好,评论可以直接定位到具体代码行,还能用 GitHub Actions 做自动检查。缺点是国内访问稳定性一般,私有仓库虽然免费额度够小团队用了,但总有些人用不习惯。
GitLab 的 Merge Request 和 CI/CD 集成度高,自托管方案对代码资产完全可控,权限管理也细。我们团队最终选的就是自托管 GitLab 作为基础平台,理由有三个:一是因为评审流完全可配置,二是细粒度权限能满足“不同项目不同评审规则”的需求,三是 Jenkins 等工具集成方便,能拿到 commit 和 merge 事件主动触发流水线。
还有一类工具是 Phabricator,Facebook 早年开源的那套 Differential 评审流,优点是审查体验非常专注,专门为“提交-审核-修订”流程优化过;缺点是界面老旧、生态相对封闭,现在基本只有一些老团队在用。
Gitea / Gogs 这种轻量级方案,胜在极速部署,适合 10 人以下的小团队或内部工具项目。重型流程跑不动,但如果只是想快速解决“代码没人看”的问题,它确实够用。我们内部的小工具脚本项目就部署过 Gitea,体验不错。
对比了一圈,我建议 10 人左右的团队直接选 GitLab 自托管,理由就一条:评审流和权限系统兼具灵活性和可控性,而且社区版功能已经覆盖了我们需要的全部场景,License 倒是其次,关键是不折腾。如果团队已经深度绑定 GitHub,也用不着迁移,GitHub 的 review 功能本身是没问题的,关键是把规则定好。
2.2 评审流程设计:从提交到合并的完整链路
工具只是骨架,流程才是灵魂。我们设计的评审流程用一句话概括就是:提交-自动检查-人工评审-修订-再检查-合入,六步闭环。每一步都有明确的负责人和通过标准。
第一步是开发者在本地完成开发,经过自测后发起合并请求。我们要求每个合并请求必须包含几个关键信息:改动描述、测试说明、风险点列表。这其实是个很逆人性的要求,因为程序员最讨厌写文档,但这一步恰恰决定了后续评审效率。在合并请求模板里把三个字段做成必填,不填就不能提交,强制几次以后大家就习惯了。
第二步是自动化流水线启动,做静态检查、单元测试、构建验证三项基础动作。这里多说一句,没有自动化检查兜底的评审流就是耍流氓。人眼盯格式错误是极其浪费的,格式类的、低级语法类的、明显逻辑漏洞类的,全部交给机器人先过滤一遍,让人工评审集中精力看业务逻辑和设计合理性。
第三步是人工评审。我们规定至少两人评审通过才能合入,一个是指定的模块负责人,另一个是随机分配的组内成员,避免每次都是同一拨人互相看,时间长了视角会固化。
第四步是开发者根据评审意见修改代码。这里最关键的规则是:每一条评审意见都必须有明确处理结果,要么修改并回复“已修改说明”,要么不采纳并说明理由,绝不允许已读不回。
第五步是修改完成后重新跑一遍自动化检查,并让原来的评审人确认。这里有个很多人会忽略的细节:修改后的代码必须重新触发 CI,而不是只跑改动的部分。有一次我们改了个工具函数,本地测试过了,结果全量构建直接挂了,因为那个函数的调用方在另一个模块里依赖了旧参数签名。
第六步是合入主干,要求使用 squash merge 合并,保持主干历史线干净。这条规则拯救了无数次的 git blame 查历史,我们一开始不强调这个,后来翻历史的时候一棵歪七扭八的提交树让人头大,才下决心改过来。
2.3 评审规则的细化和保护分支策略
流程定了之后,就要落实到分支保护规则上,光靠自觉谁都忍不住会偷懒跳过流程。在 GitLab 里面我们做了三件事。
第一件是开启分支保护,主干分支不允许任何人直接 push,包括管理员。所有变更只能通过合并请求进入。刚开始连我自己都嫌麻烦,因为有时候就想改一行文档还得走全流程,但长期来看收益远大于成本。
第二件是设置最小评审通过数,在项目设置里把批准的 required 数量设为 2。其中至少一个通过者必须是项目指定成员(通常是模块负责人),防止出现“互相点赞”的无效评审。这里有个小细节,GitLab 的审批规则支持多角色“code owner”,我们就是靠这个把模块负责人和普通成员分开算的。
第三件是合入前必须通过所有流水线检查。我们折腾过好几种方式,最后用的是 GitLab 自带的 merge checks 功能,把流水线通过和讨论已解决两个条件设为主要检查项。注意不是建议项,是必须项,凡是有未通过的 job 或未解决的讨论线程,合入按钮直接置灰。
这套规则配合一些自动化工具,比如 Danger、Commitlint 用来规范 commit message 和 PR 描述格式,开箱体验再升一档。Commitlint 的通配校验规则写起来也简单,无非是 type 限定、scope 可选、subject 非空这几条,但能保证 git log 出来整整齐齐,每一条提交信息都像是人话而不是天书。
3. 实操过程与核心环节实现
3.1 搭建评审环境的完整步骤
下面把我们的完整搭建过程过一遍,从一台空的 Linux 服务器到跑通第一次评审。假设你已经有一台 4 核 8G 的服务器,操作系统是 Ubuntu 22.04 LTS。
第一步是安装 Docker 和 Docker Compose。自托管 GitLab 用 Docker 部署是最省心的方案,不要想着裸机装,不然邮件配置、数据库依赖、定时备份能把你折磨到怀疑人生。安装命令很常规,但有个细节值得提醒:Docker 的镜像源在部分地区拉取很慢,建议提前配置一个加速源,不然gitlab/gitlab-ce这个几个 GB 的镜像够你等半天的。
# 安装 Docker curl -fsSL https://get.docker.com | bash systemctl enable --now docker # 验证 Docker 版本 docker --version第二步是用 Docker 运行 GitLab。这里最关键的是端口映射和主机名的设定。很多人第一次装 GitLab 最头疼的问题就是:网页能打开,代码却 clone 不下来。这通常是因为安装时 EXTERNAL_URL 没设置好,导致项目页面里显示的 clone 地址是个不可达的内网地址。我们用的是 8022 端口映射到容器内的 SSH 22 端口,HTTP 用 80 端口直接暴露,EXTERNAL_URL 设置为http://gitlab.example.com,后面在 DNS 或本地 hosts 里把域名解析到服务器 IP 就行。
docker run --detach \ --hostname gitlab.example.com \ --publish 443:443 --publish 80:80 --publish 8022:22 \ --name gitlab \ --restart always \ --volume /srv/gitlab/config:/etc/gitlab \ --volume /srv/gitlab/logs:/var/log/gitlab \ --volume /srv/gitlab/data:/var/opt/gitlab \ gitlab/gitlab-ce:latest第三步是初始化 GitLab 后,进入 Admin 设置,把仓库可见性设为“内部”。这样做的原因是,既然是开放式评审,就要允许团队内所有人查看代码,但又不允许外部人员随便看。在“设置 -> 可见性与访问控制”里,把“项目可见性”默认值改为“Internal”即可。
第四步是创建项目,然后进入“设置 -> 仓库 -> 保护分支”,配置保护和审批规则。区域里可以设置允许合并的角色,然后启用流水线必须成功、讨论必须解决这两个开关。
第五步是添加合并请求模板。在仓库根目录创建.gitlab/merge_request_templates/default.md,内容如下:
## 本次改动的目标 (简述需求背景,为什么需要这个改动) ## 改动范围与影响 (列出涉及的文件、模块,可能影响的功能点) ## 自测情况 (贴出自测结果,包括单测通过情况、手动测试步骤) ## 风险点与注意事项 (这个改动可能引入的风险,需要评审人重点把关的地方) ## Review 建议 (希望评审人关注的问题,比如并发安全、性能、兼容性等)这个模板用起来效果出奇的好。它给了写代码的人一个结构化的表达框架,也给了评审人一个明确的审视锚点。以前评审人经常不知道从哪里看起,现在按着模板字段一个个过就行,效率提升非常明显。
第六步是配置 CI 流水线。在项目根目录创建.gitlab-ci.yml,定义三类最基本的任务:静态检查、单元测试、构建。以 Node.js 项目举例:
stages: - lint - test - build lint: stage: lint image: node:18-alpine script: - npm ci - npm run lint only: - merge_requests test: stage: test image: node:18-alpine script: - npm ci - npm run test:unit only: - merge_requests build: stage: build image: node:18-alpine script: - npm ci - npm run build only: - merge_requests注意这里的only: merge_requests字段,它的作用是让这些检查只在合并请求里运行,避免每个人每次 push 到自己的分支都触发一遍流水线。GitLab 是支持这个语义的,如果你的 CI 版本比较老,有些写法可能需要微调,但总体思路一致:流水线是为评审服务的工具,不是压垮开发者的负担。
第七步是安装 Git 钩子或集成机器人。我们用的是 Danger,配合 GitLab CI 跑一些自动检查,比如 PR 标题是否规范、变更文件数是否超过阈值、是否包含调试代码等,让机器先干一轮粗活。Danger 的配置文件长这样:
// Dangerfile const { danger, fail, warn, message } = require('danger') const modifiedFiles = danger.git.modified_files const addedFiles = danger.git.created_files // 禁止提交调试日志 const debugFiles = danger.git.diff.split('\n').filter(line => line.includes('console.log') || line.includes('debugger') ) if (debugFiles.length > 0) { fail('不允许提交 console.log 或 debugger 等调试代码,请清理后重新提交') } // 单次变更文件数超过 30 警告 if (modifiedFiles.length + addedFiles.length > 30) { warn('这次变更涉及文件数量较多,请确认是否可以将改动拆分为更小的提交') } // 检查是否修改了 package.json 但未修改 lock 文件 if (modifiedFiles.includes('package.json') && !modifiedFiles.includes('package-lock.json')) { warn('你修改了 package.json,但没有看到 package-lock.json 的变更,请确认依赖锁定文件是否需要更新') }这套自动化规则跑起来之后,人工评审需要处理的问题瞬间少了一大半。测试、构建、格式、调试残留这些体力活全由机器兜底,人只需要做高价值的脑力判断。
3.2 评审意见怎么写才高效
流程和工具都就位了,接下来最核心的问题就是:评审意见本身怎么写。说实话,这个才是 open-code-review 能不能成的关键。工具再流畅,如果评审意见质量低,一切都白搭。
我在实际带团队过程中,总结了一套“三段式评审法”。第一段是肯定,明确说出这段代码写得好的地方,这不是客套,而是帮作者建立安全感和信任感。第二段是问题,用事实描述问题现象和后果,而不是指责作者,句式通常是“这里有一个可能导致 XX 的问题,原因大概是……”。第三段是建议,给出一个明确可执行的替代方案,哪怕只是一个搜索关键词或参考文档链接,也比说“这样写不对”有用一百倍。
举两个具体例子。低质量意见是:“这个函数写得太长了,看着累”,这种意见没有事实依据、没有后果描述、没有解决方案,作者收到只会一头雾水。高质量意见是:“这个函数有 80 行,包含了参数校验、状态更新、通知推送三步逻辑,如果每一步后续单独演化,这个函数会变得越来越难改。建议拆成三个函数,每个函数只做一件事,测试也会更好写。可以参考项目里 utils/ 下的 XX 模块的写法”。看到了吗,有现象、有分析、有方案、有参考,这才能让作者心服口服。
另外,如果有条件,尽量在代码行内评论而不是在 PR 底部写总结。行内评论可以精确定位到上下文,免去作者来回翻代码对应位置的痛苦。GitLab 的讨论线程机制做得很好,每条行内评论都挂在具体代码行上,修改后还能标记为“已解决”,整个过程有迹可循。
3.3 沟通节奏与评审会议
同步评审之外,我们还固定了每周一次的“评审复盘会议”,时长 30 分钟,主要做三件事:回顾本周合并的代码里最典型的三个评审案例,讨论评审中反复出现的共性问题,更新评审规范清单。这个会议不是诉讼大会,定调是“这是集体学习时间,不是追责时间”,气氛非常关键。有过一次我们没注意措辞,把一个新同学批评得蔫了一下午,从那以后我就特别强调点评时对事不对人,而且尽量让资深工程师先讲自己做错的案例,用自嘲打样,新人才敢开口。
这个复盘机制还有一个隐形的好处:它能把明显改进后的规范一点点沉淀下来。比如我们期间给团队加了几个新规则,都是复盘会上有人反馈“这个问题我上个月就提过了,这周又看到类似的”,才会推动把这一个规则固定下来。现在我们的评审清单越来越薄,因为真正有效的条目不是靠堆砌,而是靠活下来沉淀成的。
4. 常见问题与排查技巧实录
4.1 追求数量不追求质量
开放评审推行一个月左右,我们碰到了第一个幽灵问题:评审数量上去了,但意见质量直线下降。组里 20 个合并请求,每个都有人点“通过”,但通过的评论是清一色的“LGTM”(Looks Good To Me)。
我觉得这个风向不对,匿名做了一轮小统计,发现 70% 的合并请求在 10 分钟内就被标记“已通过”,而改动量多于 500 行的请求平均通过时间只有 15 分钟。这显然不合理,500 行代码逻辑再熟练也不可能 15 分钟看完。
当时的应对措施是“无意见不允许直接通过”。具体来说就是在合并请求页面的审批栏,如果评审人不写下任何实质性的意见备注,审批按钮不能点击——如果确实没有问题,也必须写明“本次改动范围较小,已对照检查了 XX 函数和 XX 边界条件,未发现问题”这类话。加了这个限制之后,走形式的比例立刻降下来了。
后面我还发现一个更细的问题:新人刚开始提意见往往只会提风格类的、变量命名类的,真正触碰逻辑深水区的很少。所以在体系里就明确倡导,在评审意见里给“逻辑层关注点”打个标签,例如是否考虑并发、是否可能 N+1 查询、错误处理是否充足等。出现这类意见的时候,我们甚至可以在周会上特意拎出来表扬,引导风向从“揪格式”转向“看逻辑”。
4.2 CI 流水线跑太久,评审等得焦虑
这是另一个被低估的问题。CI 配置了太多扫描步骤之后,每次合并请求光等流水线跑完就要 10 分钟,有些大项目甚至要 20 分钟。评审的人等着检查结果,开发的人等着合并,时间被白白浪费。
解决思路是把流水线分两层:快速检查和深度检查。快速检查在 push 后立刻触发,跑那些 1-2 分钟内的检查——ESLint、单元测试、构建。深度检查放到有人工批准之后再跑——如 E2E 和专门的依赖扫描。用 GitLab CI 的rules字段就能实现按触发条件分支执行,核心是通过“在 merge request 上添加 label 触发深度检查”这种方式,让日常压测工具不成为众人前行的减速带。
e2e-test: stage: test script: - npm run test:e2e rules: - if: '$CI_MERGE_REQUEST_LABELS =~ /run-e2e/' when: on_success - when: never这套机制带来一个直接改变:开发者在等待深度检查的同时已经可以进行下一项开发,而不是守着 CI 发呆。
4.3 评审意见出现激烈争执怎么办
开放评审意味着不同观点面对面碰撞,这是好事,但如果缺乏引导,很容易演变成情绪化的辩论,甚至上升到对人能力的质疑。我见过最典型的一次争执:一个后端工程师认为一个查询逻辑应该拆成两次数据库查询以保证索引命中,而一个前端转全栈的工程师认为一次查询然后在内存中拆分就够了,理由是数据量级不大。两个人讨论了两个小时没有结论,最后升级成“你根本没做过高并发系统”的人身攻击。
碰见这种情况,组里的处理原则是:用事实和数据代替观点之争。与其争论“性能会不会有问题”,不如直接下断言——“目前单表 5 万行数据,按目前增长速度一年内不会超过 20 万行,在 MySQL 上全表扫描耗时约 xx 毫秒,两次查询加内存拆分耗时约 xx 毫秒,两者均远低于接口 SLA”。把问题转化为一个可以被验证的事实核对,争执自然也就消解了。硬核原则是:技术评审拒绝个人崇拜,只认数据、复现手法和客观测试。
如果当场达不成一致,正确的做法是拉一个三方仲裁人,或者把方案两版都写出来,用 benchmark 的结果说话。在 open-code-review 的规则里,评审讨论永远不会出现“谁资历深谁说了算”,只会有“谁有数据、谁能复现、谁能拿出可执行的证据链,谁赢”。
4.4 新人不会提意见怎么办
新入组的人,尤其是刚毕业的,面对一份动辄几千行的合并请求往往不知所措,觉得“别人写得都好,我提不出问题”。这是个真实存在于所有团队的现象,不必焦虑。
我们总结了一套新人不看业务也能发现问题的方法——“防御性审视法”。用三个固定角度检查代码:边界条件有没有考虑(数组为空、超长文本、并发重复提交);错误处理是否完备(网络异常、权限不足、数据库超时);资源有没有释放(文件句柄、连接池、定时器)。这三个角度是纯工程视角的,是知识性判断,不需要多少业务背景就能看。新人们一旦从这个角度入手,通常能发现不少老手因思维惯性忽略的真实问题。
另外一个土办法是:每次评审前先把改动的测试文件打开,先看测试覆盖了哪些场景,再去反推代码逻辑。这个角度也很容易发现一个问题:如果测试文件里覆盖的场景比代码的实际分支少,说明测试有缺口,这就是一条有价值的评审意见,不需要太多业务经验就可以下判断。
4.5 历史评审记录怎么利用起来
open-code-review 沉淀下来的海量评审记录是座金矿,但如果不整理,它就是一坨裸露的“代码库泥土”。我们让每一份吸引眼球的评审讨论贴上了标签:并发、性能、安全、兼容性、可维护性,每周归纳成摘要,在周会上用 5 分钟共享。积累了一个季度之后,直接汇总成一本“团队代码评审避坑手册”,大概是几十页的密度,后续新人入职必读新手文档直接引用了这些真实案例。
同时,GitLab 的搜索其实很强,支持按评论关键词搜索历史合并请求。我给组员们建议了三个高频搜索指令:搜error handling看错误处理类讨论,搜race condition看并发类讨论,搜refactor看重构类讨论,搜索用起来,很多当前看不透的问题可能半年前就已经有答案挂在仓库的某条评论链里。
5. 实操心得与经验总结
5.1 评审文化要先于评审工具
这一路折腾下来,我最深的感触是:工具再好、流程再严,也都只是规则的容器。代码评审要真正产生价值,起作用的是团队里“大家愿意好好看代码、愿意放心表达观点”的文化。
文化的塑造其实就靠三件小事。第一,leader 要带头被 review,自己的代码放到公开区域被人挑刺,不搞特权,这点对建立信任非常关键。第二,提意见的人要养成先有理有据再说话的习惯,不贴标签、不情绪化,把每个问题讲清楚“是什么、为什么、怎么办”。第三,被提意见的人要习惯把批评当礼物,不要把“comment”等同于“指责”。
5.2 评审的质量模型:三层过滤
再聊一个我总结出的“评审三层过滤”模型。第一层是机器过滤:静态检查、单测、构建,这一层把人从繁琐的重复劳动里解放出来。第二层是规范过滤:命名是否合理,函数是否过长,模块边界是否清晰,这一层保证代码的“可读性”和“可维护性”。第三层是业务逻辑过滤:并发处理是否安全、数据一致性是否能保证、异常路径是否完备,这一层是评审的核心,也是最难培养的能力。
刚推行的时候,我们的评审内容大部分停留在第二层,能触及第三层的意见少之又少。后来我们发现问题的根源在于写代码的人自己很少把“别人会怎么看我这段逻辑”当回事,于是我们开始要求每个合并请求的描述里必须写清楚自己已经排查过的风险点,写不出风险点就说明你没认真想,审查者有权打回。这条规则之后,第三层意见的比例明显上升,因为作者自己先过了一遍逻辑坑,评审人就能顺着作者的思路去挑更深的刺。
5.3 最后再分享一个实操细节
最后给想立刻动手的读者一个小建议:不要一口气把追求“完美评审”的所有规则都推上去,哪怕你心里已经盘算好了全套方案。先做最小可行的闭环:强制保护分支、强制至少一个评审人通过、强制 CI 通过,这三条先立起来。等团队习惯了“不能直接往主干推代码”这个事实,再逐步加码:评审人数量、模板字段、Danger 规则、数据验证、复盘会议。我在第一次上线流程的时候,一次性加了十条规则,结果第一天就有人因为流程太烦而消极抵抗,后来花了两三周才安抚回来。
这套 open-code-review 机制运行一年多,最大的红利不是线上事故率降低这么简单,而是团队整体的“代码耻感”提升了。以前大家对自己写出来的乱代码没什么感觉,现在有了公开评审这面镜子,每个人都开始在意自己写的代码是否体面、是否经得起别人打开看一眼。我觉得,这大概就是代码评审这件事真正迷人的地方。