如果你在一个团队里写过代码,一定经历过类似的瞬间:提了个 PR 上去,等了两天没人理;或者在 Review 别人的代码时,盯着 diff 看了十分钟,最后只憋出一句“看着没问题”。Code Review 这件事,说起来大家都会点头说重要,可一旦落到团队日常里,常常变成流程里最容易被敷衍的那一环。我自己在几个不同规模的团队里都推行过代码审查机制,后来总结下来,真正能见效的并不是“规定谁必须审、审多久”,而是把审查这件事做成 open-code-review——公开、开放、双向的交流习惯。这篇内容就是把我在落地这套机制时踩过的坑、试过有效的做法,以及实实在在的流程和工具配置整理出来,给正在苦恼“代码审查到底怎么搞”的同学一个参考。
文章适合三类人看:刚接手技术团队、想把质量内建做起来的技术负责人;负责代码库维护、想提高 Review 效率的工程师;还有正处于“被 Review 就觉得被挑刺”阶段的新手。不管是哪个角色,下面这些内容都尽量从实操出发,少讲大道理,多说怎么做。
1. 为什么我越来越重视 open-code-review
1.1 连代码审查都做不好,谈不上质量内建
在很多团队里,质量是靠测试兜底的:开发写完代码,交给测试同学验证,发现 bug 再打回来。可这样的模式有个天然的问题——bug 被发现得越晚,修复成本越高。比如一个字段命名不规范的问题,代码评审阶段看一眼就能发现,改一行就行;可等它进了测试环境,测试同学得先复现、报 bug、写用例,开发再定位、修改、回归,一圈下来可能要花半天。代码审查恰恰是在缺陷“最便宜”的时候拦截它的手段,这也是为什么我一直把 Code Review 当作质量内建的第一步。
但这里有个前提:审查必须是真的在“审”,而不是走个形式。我看到过不少团队,PR 一提交,负责的老同事随手点个 Approve,评论区只有一句“LGTM”。这种审查等于没有,代码里的逻辑漏洞、边界条件、安全隐患,一个都挡不住。要改变这种局面,关键就是 open-code-review——把审查从“某个人的单点把关”变成“团队范围内开放透明的公共活动”。每个 PR 都是大家的代码,每个人都可以提意见,每个结论都有据可查。
1.2 open-code-review 解决的不只是 bug
把“开放”两个字加进代码审查,看起来只是个形容词,实际上改变了整个协作关系。单向的审查是检查,双向的开放是交流。我自己推行下来,发现收益远远不止“少几个 bug”这么简单。
首先是知识传递。团队里总有资深工程师掌握某些模块的深层逻辑,如果他只是默默写代码,这些经验就烂在他自己脑子里。但当大家养成开放 Review 的习惯,资深工程师在审查新人代码时提出“为什么这里用 map 而不是 for 循环”,或者给出“这个接口后面可能会有三种调用方,建议预留参数”之类的建议,这些经验就通过评论留在了代码历史里。对新人是成本最低的培养方式,对整个团队是知识不掉线的保障。
其次是减少单点风险。很多项目走到最后都会出现“这个模块只有一个人能改”的局面,原因就在于其他人从来没有深入看过这些代码。开放审查意味着每个 PR 都会被多个视角检视,看得人多了,模块就不再只属于某一个人。谁请假、谁离职,项目不会跟着停摆。
还有一点容易被忽略:开放审查能倒逼代码写得更好。人都有自尊心,知道自己写的代码会被公开讨论,提交前自然会多自查几遍,命名会更认真,逻辑会更清楚。这不是“怕丢脸”,而是健康的同伴压力。
2. 搭建一套可持续的代码审查流程
2.1 流程设计:从分支到合并,每一步都要有规则
代码审查不能靠自觉,得靠流程托底。但流程也不能设计得太重,太重了大家会想办法绕过。我的原则是:定好边界,其他事情交给工具和人自己判断。
先说分支策略。小团队十个人以内,我建议直接用 GitHub/GitLab 的常规流程:主分支长期存在,功能分支从主分支拉出来,开发完提交 PR,审查通过后合并回主分支。这种模型最简单,心智负担最小。如果发布节奏比较快、需要同时维护多个版本,那就上 Git Flow,把 develop、release、hotfix 这些分支都建起来。不过说实话,大部分团队用不到 Git Flow,硬上只会增加混乱。
然后是合并条件。我在团队里设了三条硬性规则:第一,至少一个非作者的审查者 Approve;第二,自动化检查全部通过;第三,PR 描述里有清晰的改动说明。这三条缺任何一个都不允许合并。规则的目的是让流程有底线,而不是把每个 PR 的合并都变成一场漫长的拉锯战。为了不卡住正常开发,我通常会把“必须 Approve”设为一票通过,只要有一个审查者确认就可以合并,不需要全票。
2.2 工具选型:GitHub、GitLab 还是 Gerrit
选什么工具做代码审查,会影响整个团队的体验。我三个都用过,简单说一下适用场景。
GitHub 是全球开源项目的主流选择,PR 流程天生就是为代码审查设计的。它的优势在于生态,各种自动化 bot、质量门禁插件都能直接对接。如果你团队代码托管在 GitHub,直接用它就行,不需要额外引入其他系统。
GitLab 更适合想要一体化 DevOps 的团队。它的 Merge Request 流程和 GitHub 非常相似,但内置的 CI/CD 能力更强,同一个平台就能完成从提交到部署的全过程。对于只有内网部署需求、不想用第三方 SaaS 的团队来说,GitLab 自托管是更稳妥的选择。
Gerrit 是更“硬核”的工具,Google 的 Android 项目就在用。它和前两者最大的区别是每个 patchset 都严格审查,审查通过之前代码不会真正进入代码库。这种严格模式对于大型、严谨的项目很合适,但对小团队来说,学习成本和操作复杂度都偏高。我个人的建议是:没有特殊要求,优先 GitHub;想一体化,用 GitLab;除非是做基础设施级别的项目,否则不太建议小团队上 Gerrit。
2.3 自动化检查与人工审查怎么配合
自动化检查是人工审查的重要补充,能帮 Reviewer 省下大量重复劳动。比如 ESLint 可以拦截代码风格问题,Prettier 保证格式化一致,单元测试跑完能提前发现逻辑错误,SonarQube 还能分析重复代码和复杂度。这些检查全部放在 CI 里跑,PR 里会直接显示结果。
我自己搭自动化门禁时有一个原则:自动化检查只拦“客观错误和硬性规范”,不替代人的主观判断。代码风格、格式化这类规则,机器判定比人靠谱,直接让 CI 卡住就行;但涉及架构合理性、业务逻辑、可扩展性这些需要上下文的判断,必须交给人来完成。这样分工的好处很明显:Reviewer 打开 PR 时不会被一堆空格和命名问题分散注意力,能把有限的精力放在真正重要的逻辑讨论上。
实际配置的时候,我会在仓库的/.github/workflows下放 CI 配置文件,比如:
name: CI on: [pull_request] jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 18 - run: npm ci - run: npm run lint test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 18 - run: npm ci - run: npm test加上这些之后,PR 页面会直接显示哪些检查通过、哪些失败,审查者只需要关注那些机器看不出来的问题就够了。
3. 审查者视角:怎么把 Review 意见写到点子上
3.1 审查三件事:正确性、可维护性、安全性
很多人拿到一个 PR 不知道看什么,盯着 diff 从头看到尾,看完了也说不出一二三。我后来给自己定了个框架,每次 Review 都从三个维度去看:正确性、可维护性、安全性。
正确性是最表层的,看这段代码在输入正常和异常的情况下,输出是否符合预期。重点看边界条件、空值处理、并发场景。比如一个删除接口,参数传进来一个不存在的 ID 怎么办?事务回滚了吗?这些地方最容易藏 bug。
可维护性更考验经验。要看这个改动是否容易理解和修改,有没有重复代码,函数是否过长,命名是否清晰。有个简单判断标准:如果三个月后你再看这段代码,不用看 PR 描述,能不能一眼看懂它做了什么?如果不能,说明它不够可维护。
安全性是很多人容易忽略的维度。尤其是涉及用户输入、权限校验、外部接口的地方,要问自己几个问题:这个接口有没有做鉴权?用户输入有没有被转义?SQL 有没有注入风险?日志里有没有把敏感信息打出来?有些问题在功能测试阶段根本暴露不了,只能靠审查时带着安全视角去扫描。
3.2 写出有建设性的评论,而不是“我不喜欢”
Review 评论的语气和内容,直接影响作者的接受度。我在早期吃过亏,写过“这里写得不好”“这个设计有点问题”这种模糊又带情绪的话,结果作者看了很抵触,双方在评论区来回拉扯,最后问题也没解决。后来我学到一个方法:评论的时候尽量给“问题+建议+理由”的三段式。比如:
“这个方法目前接受了三个参数,调用的时候很容易搞混顺序。建议改成传一个配置对象进来,这样调用方不需要记忆参数顺序,后续加参数也不用改方法签名。”
这种写法比“参数太多了”更有用,因为作者收到了具体可执行的建议,也理解了背后的原因。如果有什么地方只是风格偏好,而不是原则性问题,我会明确标注“non-blocking”,告诉作者这个问题不阻塞合并,你自己权衡。这样能有效减少无意义的争论。
3.3 我常用的审查检查清单
把经验沉淀成清单,是让 Review 质量稳定的最有效方式。我平时审查时会在脑内过一遍这份清单,遇到大型或重要 PR 还会写在评论里,让作者知道我从哪些角度看了代码:
| 检查维度 | 具体关注点 |
|---|---|
| 逻辑正确性 | 边界条件、异常路径、状态流转是否符合预期 |
| 数据一致性 | 事务是否完整、缓存与数据库是否可能不一致 |
| 安全性 | 鉴权、输入校验、敏感信息泄露、依赖漏洞 |
| 可读性 | 命名、函数长度、注释是否解释了“为什么” |
| 性能 | 循环内是否有多余查询、是否有明显的时间和空间浪费 |
| 测试覆盖 | 新代码是否有配套的单测,核心分支是否覆盖 |
这份清单不是死规矩,更多是给大脑一个提示。你可以在团队内部文化里把这份清单贴出来,大家 Review 的时候照着它过一遍,很快就能统一团队的标准。
4. 开发者视角:如何让代码更经得起审查
4.1 提交前的自查:把 Reviewer 当第一次读代码的人
Reviewer 的时间很宝贵,你在提交前多花半小时自查,就能帮对方省下两小时甚至更多。我把提交前的自查理解成:假设 Reviewer 从没看过你的功能,只通过代码本身,能理解你做了什么吗?
自查的第一步是重新读一遍自己的 diff。很多人在提交后才发现“这里少了个分号”“那里有个调试日志忘了删”,这些低级错误如果出现在 PR 里,会严重影响审查者的判断,让他觉得你的代码整体不靠谱。所以提交前至少花几分钟过一遍改动,把明显的低级问题清掉。
第二步是跑一遍测试和构建。永远不要在本地测试没过的情况下提交 PR,这是节约团队时间的基本礼貌。测试通过之后,再想想有没有边界情况没覆盖到,比如数组为空、输入为 null、网络超时。花点时间补上这些单测,Reviewer 看着会放心很多。
4.2 PR 描述写得好,审查效率翻倍
PR 描述是我非常看重的一项内容。一个好的 PR 描述,应该能回答三个问题:这个改动解决什么问题?改动的核心思路是什么?怎么验证这个改动?
我常用的 PR 模板长这样:
## 背景 <!-- 说明这次改动要解决的问题,附上需求链接或 issue 编号 --> ## 改动内容 <!-- 列出主要改动点,尽量用列表说明,方便 Reviewer 对号入座 --> ## 测试情况 <!-- 跑过哪些测试、手动验证过哪些场景 --> ## 其他说明 <!-- 有没有需要 Reviewer 特别关注的代码段,或者已知的取舍 -->别小看这个模板,它能把审查者从“猜你在干什么”里解放出来,直接进入“看你有没有做对”的状态。特别是“其他说明”那一栏,如果你有意识地标注了哪些部分希望 Reviewer 重点看,对方会很感激,因为这意味着你替他省了精力。
4.3 收到差评别急着辩驳,先理解再回应
收到 Review 里的负面评论时,第一反应很容易是“他是不是不懂我的思路”。这个心态我太熟悉了,我也经历过。但后来我调整了应对方式:先假设评论有价值,花两分钟理解对方为什么提这个问题,再想一下自己的代码里是不是真的存在这个隐患。
如果对方说得对,就大方承认,直接改代码并回复一句“你说得对,这里确实有问题,已经修改”。这样不仅解决问题,还会让审查者更愿意继续认真审你的代码。如果对方提的意见是误解,也别带着情绪反驳,摆事实讲道理就好。比如对方认为某段逻辑有 bug,你可以补充具体的输入输出例子来解释。
记住,审查的过程是协作,不是输赢。双方的目标都是让代码变好,而不是在这场讨论里证明谁更厉害。
5. 常见问题与排查技巧实录
5.1 流程中常见的坑
推行代码审查的过程中,我遇到过不少典型问题,挑几个最常见的说一下。
第一个是“PR 太大,审查者根本看不完”。一个 PR 改了上千行,涉及十几个文件,再负责的审查者也会失去耐心,最后要么草草 Approve,要么一直拖下去。解决方法是要求“小步提交”:把大功能拆成多个逻辑独立的 PR,每个 PR 尽量控制在 300 行以内。如果实在拆不了,也要在 PR 描述里写明阅读顺序,引导审查者按模块看。
第二个是“Review 评论堆积没人处理”。有时候审查者提了一堆意见,作者却一直不回应,PR 挂在那一两周没动静。我在团队里的做法是:审查者在 Approve 之前,评论会阻塞合并,这样作者为了推进工作,自然会被推着去处理评论。如果评论里的建议真的不采纳,也要养成回复“谢谢建议,这里因为 XX 原因不修改”的习惯,让讨论有个明确的收尾。
第三个是“把 Review 当人身攻击”。新人尤其容易产生这种感觉,被批评后心态崩了,写代码越来越束手束脚。这里需要团队管理者在文化上去做引导,让大家把评论看成是对代码的讨论,而不是对人的否定。可以在团队例会上分享一些真实案例,把“这条评论其实是建议,作者改完之后代码确实更好了”的过程摆出来,慢慢扭转氛围。
5.2 从“抵触审查”到“主动提 PR”的落地办法
多数团队刚开始做代码审查时都会抵触,因为大家觉得“多了一道等待审批的手续”。我试过一种有效的方式:先把审查的收益“量化”给团队看。
具体做法是记录一段时间的线上故障,每周复盘,把故障原因归因到“如果当时有代码审查,这个问题能不能被拦截”。坚持一个月,团队会发现很多线上问题其实在 PR 阶段就能暴露出来。这时候再去推严格审查,大家抵触感会小很多。
另外,管理者要以身作则。技术负责人提交的 PR 也要按流程走,也要被别人挑刺,甚至在别人没意见时主动拆解自己的设计思路,把“如何做一次高质量审查”演示给团队看。这种东西言传不如身教,负责人带头示范,团队很快会跟着学。
5.3 顺手提高效率的几个 Git 操作
最后分享几个审查时常用的 Git 操作,都是我在日常工作中高频使用的。
看 PR 的分支差异,可以用:
git fetch origin main git diff main...feature/login注意三个点main...feature/login表示只看这个功能分支新引入的改动,而不是两个分支的全部差异。如果你已经在功能分支上,也可以用:
git diff main...HEAD要看某个文件的修改历史,辅助理解为什么这样写,用:
git log -p --follow src/utils/request.js | less还有一个小技巧:Review 完想让代码合到本地验证运行效果,用:
git checkout feature/login git pull npm install && npm run dev这些命令都很基础,但是能明显提升审查时的操作效率。审查不是只在网页上看看 diff,真正让人放心的是把代码拉到本地跑起来,边跑边看逻辑。
我在实际使用过程中的体会是,代码审查这件事,特别忌讳把它看成“找茬”或者“走形式”。它最理想的状态,是团队里每个人都在认真看别人的代码,也欢迎别人认真看自己的代码。这个习惯一旦养成,你会发现它带来的不只是更少的 bug,还有更顺的协作、更快的成长,以及团队里说不清道不明的信任感。最后再分享一个小技巧:如果你想让 Review 文化更快落地,可以从自己负责的 PR 开始,主动在描述里写明“这段代码我不确定,请重点看”,你会发现这个小小的示弱,会让整个讨论的氛围变得完全不同。