代码审查这件事我在三个团队里做过三种完全不同的姿势。第一个团队基本不审合并全靠自觉线上事故一年几次第二个团队审了但流于形式评论里全是“LGTM”和“变量名建议”第三个团队才真正把代码审查做成了质量闸门。所以每次有人问我代码审查到底有没有必要我的回答从来不是“很有必要”这种空话而是反过来先问一句你打算怎么做本文就围绕代码审查的必要性和最佳实践把我踩过的坑、试出来的流程、沉淀下来的检查清单一次讲清楚。它适合刚开始引入审查的团队也适合那些已经审了但觉得没效果的开发者和技术负责人。1. 代码审查的必要性它到底在解决什么问题1.1 质量闸门给代码配上第二双眼睛很多团队的第一反应是我自己写代码的时候已经很小心了单元测试也写了CI也过了代码审查不是多此一举吗这里有个根本误区编译器和静态检查只能抓语法、格式、空指针这类规则明确的问题但抓不住“这个接口设计不合理”“这个分支逻辑走不到”“这块复杂度以后没人改得动”。它们只是代码的“拼写检查”不是“逻辑校对”。代码审查的核心价值是让另一个拥有上下文的人重新读一遍变更验证“需求是不是真的被正确实现了”而不是“代码看起来能不能跑”。我自己写代码时容易陷入思路惯性会顺着自己的脑回路默认为某段逻辑是合理的这个盲区靠自己是很难看出来的。换一双眼睛之后很多原本被忽略的边界条件、异常分支、并发冲突都会暴露出来这就是代码审查最朴素的必要性减少由于思维惯性导致的“想当然”。我经常拿写文章打比方。自己写完一篇文章检查三遍依然会漏掉错别字和逻辑断层但别人一眼就能看出来。代码比文章更严格因为代码里的微小逻辑错误不止影响一份稿件它可能影响一次支付、一批订单、一台设备的运行。代码审查就是那个在你发出“稿件”之前帮你认真读一遍的人。1.2 缺陷成本越早发现成本越低为什么要花人力去提前看这些“还没爆发的bug”因为缺陷的修复成本是随着发现阶段往后推移而快速上升的。设计阶段发现需求矛盾改一页文档就行编码阶段发现逻辑问题改几行代码重跑测试就行代码审查阶段发现提交前改完、补上测试成本和编码阶段差别不大。但等合并进了主干测试阶段才发现你可能要花半天去定位、复现改完还要做回归验证。如果一直漏到线上那就是事故用户反馈、日志排查、紧急回滚、修复发版、复盘报告一次线上故障消耗的人力往往超过十次代码审查的总和。我团队里有过一次很典型的例子一个活动配置系统因为一个边界条件没被关注当时在审查会上被提了出来提交者在合入前补了两行判断。结果第二周线上真的出现了类似的数据异常但已经被提前拦住了。那次之后组里基本没人再质疑“审查到底值不值得花时间”因为大家都明白审查阶段发现问题的成本几乎为零而线上发现问题时成本是几十倍、上百倍。这也是我理解代码审查必要性的核心逻辑它不是额外增加的工作量而是把“未来可能要花的时间”提前支付了一部分。用流行的说法叫做“时间换空间”把问题的发现从线的末端挪到源头这比事后救火要划算得多。1.3 知识传递审查是异步结对编程代码审查另一个常被低估的作用是知识传递。一个后端工程师写完支付模块另一个同事在审查时问“这里为什么这样设计”作者要解释审查者要理解这个过程中双方都完成了对这段代码的共同认知。以后这块代码出了问题不再只有作者一个人能救火至少还有一双曾经认真看过的眼睛。这个价值在团队人员流动时格外明显。如果某个模块只有一个人写过、看过、维护过其他人都不敢碰那就是典型的单点故障。哪怕没有人员离职一旦负责人休假模块就进入停滞状态。如果模块每一行代码都经过另一个人的审查即使那个人没有亲手写他对核心链路、异常处理、边界条件也有基本了解接手成本会低很多。从这个角度看代码审查还是一种可积累的组织资产。团队里每个人读过的PR、提出的问题、被修正过的理解都会变成隐性知识沉淀下来。长期坚持之后新人能通过阅读别人的PR快速学会团队写法老人能在解释自己代码的过程中重新审视自己的设计。这也是为什么我一直坚持代码审查的最佳实践里一定包含心态上的转变——它不是“检查你有没有做好”而是“我们一起把这段代码弄明白”。1.4 反过来推动大家写出更清晰的代码很多人没注意到的是代码审查本身会改变作者写代码的方式。当你知道这段代码会被另一个人逐行检查时你会不自觉地做几件事把变量命名理清楚把无关的改动拆开把复杂的函数拆成更容易解释的小函数把注释写在真正需要解释的地方。这种“因为要给人看所以写得更认真”的效果是一种正向的自我约束。就像你知道桌上要放一份给客户看的文档时自然不会随手写一堆乱七八糟的草稿。代码审查做得越多团队代码的可读性就会越好因为每个人都学会了站在读者的角度写代码。这也是很多团队在做了一两个季度审查后普遍发现的一个事实审查意见变少了不是因为大家不再认真看而是因为提交者的代码质量本身提高了。2. 为什么多数团队的代码审查流于形式2.1 心态跑偏把审查当成审批或者挑刺代码审查最容易死掉的第一种心态是“审批心态”。审查者觉得自己是门卫生怕出了问题背锅于是不信任任何提交要么不看内容直接打回要么找到一点小毛病就要求修改逼得作者不得不防御性修改。另一种心态是“挑刺心态”审查者专门盯命名、缩进、注释评论冒出一大堆“这里应该加个空格”“这个名字看不懂”却对真正的逻辑漏洞视而不见最后作者被淹没在琐碎的意见里身心俱疲。这两种心态都会让审查迅速变成形式主义。正确的出发点应该是审查者不是最终裁决人而是这段代码的共同责任人。你评论的是代码不是人你的目标是帮助这次变更变好而不是证明自己比作者高明。具体到评论方式上我建议多用提问而不是命令多说“这里会不会出现某种情况”而不是“你这个写法不对”。提问式评论天然带着讨论的味道命令式评论容易把整个PR变成辩论赛。我见过太多团队最后Review模块里的对话全是“建议修改”“已修改”“同意”却没有真正的讨论这就是典型的形式主义。代码审查如果失去了“讨论感”那它和机器跑一遍质量检查没有任何区别。2.2 流程设计败笔一个PR塞进几千行代码审查流于形式的第二个常见原因是变更本身拆得太烂。我见过一个PR同时包含一个数据库表结构变更、一个接口逻辑重写、一次前端样式调整还顺带做了几轮代码格式化总变更量超过三千行。这种PR根本没法审reviewer看完前一百行就已经晕了后面只能目送通过。要让审查真正有效有一个绕不开的最佳实践小步提交单一职责。一个PR尽量只做一件事功能开发和重构要拆开重构和格式调整也要拆开。新功能尽量拆成几个有顺序、有依赖关系的小PR按依赖顺序合并。这样做的好处是每一次合并都可以独立审查、独立测试、独立回滚出问题的时候也更好定位。同时PR描述本身也是流程的一部分。如果一次提交的描述只有一句“fix bug”审查者连背景都要靠猜那审查的门槛就被人为抬高了一大截。好的流程会让提交者主动提供足够的上下文让审查者把精力放在判断上而不是放在考古上。2.3 没有自动化兜底人工全去看琐碎问题第三个失败原因是人工承担了太多本该由工具完成的事。如果一个团队连格式化、Lint、基础单元测试都没有接入自动检查reviewer就不得不花大量时间去评论“这里多了个空格”“缩进不对”“import排序要调整”。这些意见本身没有错但它们是机械的、低密度的很快会把审查者的精力和耐心消耗完。正确分工是机器能判定的问题尽量在PR阶段就由自动检查拦住不要让人来看。人工审查应当聚焦在机器做不到的事情上业务逻辑是否正确、边界是否安全、设计是否合理、后续是否好维护。前面也说了静态检查能告诉你某个变量可能为空但它不会告诉你这个位置返回空值是不是一个合理的设计选择CI能跑通单元测试但它不会思考这项变更是否偏离了需求本意。自动化是审查的前置过滤器它可以减少人看的噪音但永远替代不了人看。2.4 审查结果没有闭环形式主义还有一个典型特征合入后评论里的问题没有责任人。作者可能看到了评论但不知道哪些是必改哪些是建议审查者也懒得追问最后代码带着一堆未解决的讨论项就进了主干。一次两次之后大家默认“Review就是走过场”下一次提交和审查都会变得更敷衍。所以流程里必须明确闭环规则必须修改的项不处理完PR不准合并作者要逐条回应评论哪怕回复“不改原因是……”也算回应平台如果没有强制拦截团队也要约定一个“红线”。这个话题在下一部分讲最佳实践时会具体展开这里先给出一个判断标准你们团队的合并请求里是否还残留着未解决的讨论如果有说明流程已经松了。3. 代码审查最佳实践从提交前到合并后的完整流程3.1 审查前把PR描述写成“队友读得懂”的样子高效的代码审查一半的工作在提交者这边。我见过最快的审查几乎打开PR描述就已经知道要看哪里、重点检验什么我也见过最慢的审查reviewer要在几百条提交记录里翻背景来回问“这个问题从哪来的”“为什么要动这里”。差别不在审查者的水平而在提交者给的上下文。一个实用的PR描述模板可以覆盖我总结的四件事目的、改动概览、测试说明、风险与注意事项。部分要写什么为什么需要目的与背景这次改动解决什么问题关联什么需求或工单让审查者知道验收标准是什么改动概览改了哪些模块、哪些文件变更如何组织让审查者快速建立代码地图测试说明手工验证了哪些场景自动测试覆盖哪些用例减少审查者的重复验证成本风险与注意事项可能影响哪些调用方、回滚方案、降级策略让审查者聚焦在风险最高的区域我团队里公开推荐的模板大致长这样## 目的 一句话说明这个PR要解决什么问题。 ## 背景 补充问题场景、相关需求、历史尝试方便reviewer快速理解。 ## 改动点 按业务模块或目录层级列出变更点不要只是列文件清单。 ## 测试验证 - 单元测试新增XX用例覆盖XX边界 - 手工验证运行XX命令构造了XX数据 ## 风险说明 - 是否影响已有接口/数据存储 - 是否存在迁移/回滚风险 - 是否有需要灰度观察的点 ## 自检 - [ ] 已运行本地格式化与静态检查 - [ ] 已补充/更新相关测试 - [ ] 已在目标环境完成冒烟验证别小看这个模板很多团队把PR描述质量提升起来之后审查响应时间肉眼可见地下降。因为审查者在拿到一个描述清晰的PR时不需要先花20分钟读上下文直接就能给出有质量的反馈。3.2 审查中分层看代码而不是从头扫到尾很多reviewer的习惯是从第一个文件点到最后一个文件这其实很低效。更好的做法是分层审查每一层关注不同维度。第一层看整体设计。这个改动放在当前模块是否合适接口设计是否合理有没有重复造轮子有没有新的循环依赖是否在错误的位置处理了逻辑这一轮不需要逐行读先把核心文件的接口、抽象、调用关系看明白。第二层看关键逻辑。进入核心函数后逐行读数据流和分支重点关注边界条件、异常分支、并发场景、权限校验、事务边界。这个阶段才是真正体现代码审查专业性的地方也是发现问题最多的环节。第三层看杂项。命名、注释、死代码、调试日志、临时标记等。能自动化处理的交给自动化人工在这一层只花少量时间重点是看那些影响后续可维护性的点。为了方便落地我把常用检查点整理成了清单。审查不同性质的PR时不需要每次都完整过一遍按变更类型挑重点就行。例如改动配置时重点查安全和回滚改算法时重点查边界和性能。检查维度常见问题举例逻辑正确性分支条件是否写反、边界值是否取错、状态转换是否完整边界与异常空值、超长、并发、重复请求、部分失败如何处理安全输入校验、权限控制、越权访问、敏感信息泄露性能循环内查询、N1问题、大对象加载、缓存缺失可测试性是否能方便地构造输入输出是否容易单测 lokal一致性命名、风格、错误处理方式是否与现有代码一致可维护性功能是否过于集中、注释是否解释“为什么”而非“是什么”审查过程中写评论也有技巧。不要用多种不同表达重复同一个意见尽量合并成一条总览不要看到小问题就立刻零散评论先看完整段代码再一起提。评论原则上区分优先级我习惯用三级必须修改不修会出现bug、安全隐患或明显偏离需求。这类评论不解决不允许合并。建议修改不是缺陷但会影响可维护性、一致性。作者可以不改但需要给出理由。纯建议个人偏好可做可不做。作者可以简单回复避免话题无限延伸。这种分级最大的好处是让作者知道哪些问题需要立刻处理哪些可以放一放避免在“作者觉得没必要、审查者觉得不放心”的灰色地带反复拉扯。3.3 审查后作者要主动闭环别让PR“死”在列表里作者收到评论之后最忌讳的是默默改完代码就重新推送不解释、不回评论、也不标记讨论状态。审查者看了看发现之前的疑问没有任何回应只好再问一遍提问者觉得“你根本没看我的评论”被提意见的又觉得“我改了但你没看到”整个PR就在这种沟通损耗中拖了很久。好的闭环动作是作者对每条评论给一个明确回应。“已修改提交在新版本中”“不修改原因是……”“这个意见我记录了后续单独优化”都算有效回应。尤其对“建议修改”和“纯建议”类的意见一句“明白了先不处理”比沉默好得多。修改完成后主动重新触发review不要等着别人发现你更新了。很多平台都有重新请求review的功能作者要用起来否则PR会埋没在长长的列表里白白增加排队时间。合入之后如果这次变更后来引发了回滚或线上问题不要只归因到作者身上可以把原因补到团队的知识库里看看是审查时漏了哪类问题流程上缺了哪个环节。这不是追责而是让代码审查这个习惯持续进化。3.4 自动化与人工怎么分工我在第二部分说过没有自动化兜底的审查最后都会耗死在低价值评论上。所以最佳实践中一定要有清晰的自动化分工提交者在推送后自动检查先跑不通过根本不进入人工审查环节。检查项可以包含格式化、Lint、静态分析、单元测试、构建和部署前校验。这些都是事实校验机器做比人做稳定得多。自动化跑完之后剩下的时间才轮到人做价值判断。人的优势在于理解上下文这个需求为什么这么定义这个接口的未来演进是什么这个改动的长期维护成本能不能接受。我需要强调一个原则人的判断不能被自动化完全替代。比如一个AI辅助工具可以帮你扫出很多类似的模式问题但它是基于统计不是基于你对业务的理解它提出的“建议”可能听着合理但放到当前业务背景下反而是错的。我现在的习惯是把AI工具当成“第一遍草稿审查”让它快速标出明显的安全风险、重复代码、可疑逻辑我再根据业务上下文做二次判断。真正重要的PR最后拍板的一定是人。自动化负责提高效率人负责兜底质量这两者不是二选一而是流水线上的两个环节。4. 常见问题与排查技巧让代码审查真正落地4.1 审查积压、PR排队怎么办很多团队失败在审查响应太慢。早上创建PR下午没人管第二天还在队列里作者干脆在私聊里挨个求人看。这种情况下审查体验极差大家逐渐把PR推到最后一起批量合并审查自然名存实亡。要让审查保持节奏建议给首次响应定一个SLA。比如“24小时内必须有人第一次回复哪怕结果是‘我看完了逻辑上没问题等CI过就合’”。注意这里的SLA是首次响应不是要求24小时内审完。它传达的信息是“你的PR没有被人遗忘”这对作者的体验非常重要。如果经常出现没人看的情况说明审查负担分配有问题。可以试每周指定一个“review值班人”专门负责当天新提交PR的初审和分发也可以每周站会花五分钟过一遍open PR列表由值班人重新分配Reviewer。如果某个PR阻塞超过两天升级给技术负责人判断是否继续等、还是安排人手支援。还有一个常见的坑是“紧急修复”被滥用。真正的线上事故可以走紧急通道先合并后补审但每次这么做都应该登记为一笔“review债”24小时内补齐审查和事后说明。如果紧急通道每周用上好几次说明你们的流程不是紧急而是日常没有走对路。4.2 风格争论和“我更喜欢”问题代码审查中最容易引发情绪消耗的争论往往不是逻辑问题而是风格偏好。A说“这里应该用early return”B坚持“现在的if嵌套没问题”A说“函数名应该叫extract”B认为“parse更直观”。两个人各有各的道理最后为了一个不影响正确性的命名来回拉锯一个PR拖上两三天。我处理这种分歧有一个硬性原则编码规范里明确写了按规范走不要在PR里反复讨论规范里没写的提议者不能把它当成“必须修改”的阻塞性评论。如果你希望这个风格以后成为团队标准正确做法是单独提一个规范变更在团队评审中讨论而不是借某个PR的评论区推行个人偏好。规范的更新也要有版本记录。一旦某个风格问题被反复提出就说明它值得被写进规范文档哪怕只是写一条“无特殊理由时使用某种写法”。下次再有人提同样的问题直接给链接就不用在评论里翻来覆去说原理了。规范不是用来限制讨论的而是用来终结重复讨论的。4.3 新人不会审、不敢审怎么办不少团队里新人是不参与审查的原因是“刚接手别人的代码我看不懂怎么敢提意见”。这个心态其实可以理解但也恰恰是错误的使用方式。新人提问的价值不在“提得对”而在“能暴露不清楚的地方”。很多你以为“基本常识”的代码在新人眼里其实很难理解而“别人看不懂”本身就是可维护性差的信号。我给新人建议的第一种评论方式就是提问。不需要你下结论不需要你说“这里错了”只需要问“这里为什么不用已有的那个函数”“这块逻辑我想了一下没明白能不能加个注释”这种问题很容易被作者接受也能让作者意识到自己的代码在别人眼中长什么样。想培养新人的审查能力最好的方式是“先结对后独立”。新人跟着一位有经验的同事一起看一个PR看对方先看哪些文件、怎么判断优先级、如何写评论然后让新人自己独立描述一遍这个PR的逻辑。独立审查的最开始可以从优先级最低的“纯建议”开始慢慢过渡到能提出“必须修改”级别的问题。这个过程不用急让它自然发生。4.4 用数据找瓶颈而不是用数据考核人我曾见过一个团队引入了完整的审查工具和平台但很快大家就不愿意认真写评论了。原因很简单管理者开始统计“每个人提了多少条意见”“每条意见修改了几次”并把数据当成变相的绩效考核。于是审查者开始刷评论数量被审者开始委屈整个项目很快退化成一场症状式的表演。这件事给我留下的教训很重。度量数据本身没有错错的是使用方式。我建议记录三类数据就够了PR首次响应时间、PR从创建到合并的耗时、未解决问题数量。这三个指标是用来找流程瓶颈的比如“响应慢是因为人员分配不均”“合并慢是因为PR经常拆得太大”“未解决问题多是因为评论分级不清晰”。不要拿这些数据去给个人排队、打分、排名那样只会培养出一切以“过关”为导向的形式主义。最后再分享一个我坚持了很久的小习惯每隔一两周做一次“Review复盘会”。拉出最近合并的10个PR公开讨论哪些评论是有价值的哪些是噪音哪些类型的bug在审查中反复漏掉。通过这种复盘团队会自然沉淀出新的检查项和规范也会让每个人都觉得审查真的在起作用。我在实际项目里的体会是一旦这种习惯建立起来代码审查就不再是流程制度而是一种团队自觉它给团队带来的影响远不止减少bug那么简单。
阅读完成 · 觉得有帮助?