ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

代码评审实战指南:从流程设计到自动化落地

代码评审实战指南:从流程设计到自动化落地 1. 为什么代码评审从可选项变成了必需品1.1 最典型的代码评审失败场景先从一个我亲历的场景说起。入职第二家公司的第三周我被拉进一个线上事故复盘会。事故原因很朴素一位同事在改接口参数时顺手把一个状态判断的逻辑从改成了原因是这样更符合业务语义。他提交的时候没有走评审直接在群里喊了一声我合并了大家拉一下最新代码。结果当天下午所有边界条件下的订单状态全部错乱累计影响了几千笔订单。这场事故之后团队定了一条铁律所有代码必须经过评审才能合并。但真正执行起来的第一个月效果却非常糟糕——评审变成了走过场Reviewer 打开 PR 看到变更文件超过 30 个扫一眼就点了 Approve留言永远只有一句LGTM。这不是个案。几乎所有团队在推行代码评审时都会遇到同样的困境评审流程从没有到有很简单但从有到有效非常难。而 open-code-review 这类开放式的评审实践项目本质上就是想解决评审无效这个问题。1.2 代码评审真正的价值不在找bug很多人对代码评审有个误解觉得评审就是为了抓出代码里的 bug。如果你用这个目标去推评审很容易失望——因为大多数评审确实抓不到什么严重的 bug测试和线上监控才是抓 bug 的主力。我在实际推行评审三年之后对代码评审的价值排序有了完全不一样的理解。按重要程度排是这样的第一价值消除信息孤岛。代码评审是团队里唯一一个强制性的知识同步机制。你写了什么、为什么这么写、改了哪些边界都得在评审里讲清楚。这一轮下来至少有三五个人对你的代码有基本认知以后这块逻辑出了问题有人能接住。第二价值守住架构底线。写代码的人容易陷入局部优化——为了完成这个需求顺手绕开之前的抽象直接改底层函数。评审是唯一能站在全局视角说你这个改法会破坏别的地方的关卡。第三价值形成团队共识的代码规范。规范文档写得再细也没人读。但评审里每一次这里为什么不走 utils 里的公共函数的留言就是一次活生生的规范教育。第四价值才是发现缺陷。逻辑错误、漏掉的边界条件、并发问题这些确实能在评审中发现一部分但绝不是主要产出。想清楚这个排序你才会明白为什么很多团队评审流于形式——因为他们只盯着第四点觉得反正没抓到 bug评了个寂寞然后越来越敷衍。2. 设计一套适合自己的评审流程前置条件与角色分工2.1 评审流程的整体设计思路open-code-review 这个理念里有一个核心原则评审流程的复杂度必须与团队规模、代码重要程度相匹配。一个三人创业项目和一个人数过百的中台团队评审流程绝对不应该一样。我见过最典型的反面案例是小团队照抄大厂的评审规范要求每个 PR 必须两个 Reviewer、必须关联需求单、必须通过全部自动化检查、必须 24 小时内完成评审。结果就是评审周期比开发周期还长整个团队的交付节奏被拖垮。设计评审流程之前先回答三个问题你的团队有多少人人数决定了评审角色的分工粒度。三五个人不需要区分主要评审者和次要评审者所有人互相看就行。十五人以上就需要明确每个模块的模块负责人来兜底。你们的代码变更频率多高每天几个 PR 和每天几十个 PR 是两种完全不同的评审节奏。高频场景下必须在自动化检查上多投入把人工评审的负担降到最低。哪些代码改动必须走完整评审哪些可以走轻量评审这是一个非常重要但大多数团队没想清楚的维度。依赖升级、配置变更、文档修改、格式化调整这类改动完全不需要走全量人工评审浪费时间。我建议按变更类型区分评审等级而不是一刀切。在我设计的方案里评审分成三个等级等级适用场景评审要求轻量评审文档、配置、格式化、纯重构无行为变化一个 Reviewer 即可24 小时内完成不需要过会标准评审普通功能开发、bug 修复至少一个模块负责人 一个熟悉相关逻辑的同事含自动化检查重点评审核心链路、架构调整、数据迁移、安全相关改动至少两个资深 Reviewer必须线下过会逐行评审这个分级最大的好处是团队不会因为所有改动都要走同样复杂的评审而产生抵触情绪同时核心代码的评审质量有保障。2.2 角色分工作者、评审者、维护者的边界评审里的角色边界不清晰是流程混乱的主要原因。最常见的现象是所有人都是 Reviewer所有人都只提意见、不对结果负责最后代码合并的责任被稀释成大家都有责任。我的经验是必须明确三个角色作者Author负责准备评审材料、主动说明改动的背景和风险点、回应所有评审意见。作者的职责不是求通过而是把评审者需要知道的上下文全部提供清楚。评审者Reviewer负责从代码质量、逻辑正确性、架构一致性三个维度审查变更提出具体、可执行的修改建议而不是泛泛地说这里写得不好。合并维护者Maintainer部分团队也叫 Merge Master这个角色最容易被忽略。维护者不一定要参与每一行代码的讨论但他必须确认所有评审意见都有明确结论已解决 / 明确不修改 / 后续跟进然后执行合并。维护者通常由该模块的负责人担任。这三个角色可以有兼任但在同一个 PR 里最好别让作者自己去合并自己的代码。哪怕是一个人的开源项目让一个信任的伙伴来过一眼再合并出问题的概率都会小很多。还有一个细节值得提评审者不应该超过三个人。人多了反而没人真正看代码大家都觉得反正有别人在看。我在团队里明确规定了这一点超过三个人的评审请求会被拒回来要求作者选最有价值的评审者。2.3 评审的粒度与时机评审的粒度——也就是一次评审对应多大范围的代码变更——是影响评审质量的关键因素。这里有一个铁律变更范围越小评审效率越高评审质量也越高。我在团队里做过一次统计PR 改动行数在 50-150 行之间时评审者真正投入到细节阅读的比例最高一旦超过 300 行评审者开始扫读超过 800 行基本就是直接 Approve 了。所以我们的硬性要求是一个 PR 的改动尽量控制在 300 行以内超过的部分要拆分成多个 PR或者先在 PR 描述里说明拆分的理由。评审的时机也很有讲究。我的建议是代码合并到主干之前必须完成评审这个之前可以是分支提交后、也可以是合并到集成分支之前。但有一种做法会毁掉评审的意义——就是先合并再补评审。一旦代码已经进了主干评审意见的紧迫感就会消失Reviewer 提的意见大概率会被当成建议而不是必须处理最后沦为一堆永远不关闭的评论。3. 从零搭建自动化评审工作流3.1 环境准备与工具选型代码评审不能全靠人工盯着自动化检查是评审流程的地基。我在实践 open-code-review 时的思路是先把所有机器能查的检查项全部自动化让人工评审只关注机器查不出来的东西——逻辑设计、架构一致性、业务语义。这一节我以一个典型的 Git 托管平台GitLab 或 GitHub 均可为例讲一下自动化评审工作流的搭建过程。以下命令以 GitLab 为例GitHub 流程类似。首先是仓库层面的自动化检查配置我常用的.gitlab-ci.yml结构长这样stages: - lint - test - build lint: stage: lint image: node:18 script: - npm install - npm run lint only: - merge_requests test: stage: test image: node:18 script: - npm install - npm run test:coverage only: - merge_requests build: stage: build image: node:18 script: - npm run build only: - merge_requests这里的关键配置是only: merge_requests意思是只在有合并请求即 MR/PR时触发流水线。这样做的好处是开发者在自己分支上频繁提交代码不会触发重复检查只有真正要合并时才启动整套自动检查节省资源也避免开发者在等待检查结果上浪费时间。除了构建和测试有两类自动化检查很容易被忽略但对评审质量的提升非常明显。第一类是静态代码分析。以 JavaScript/TypeScript 项目为例ESLint 搭配eslint-plugin-import可以自动检查未使用的变量、未导入的依赖、循环引用等问题配合 SonarQube 或 CodeClimate 这类工具还能进一步分析重复代码、圈复杂度、潜在 bug 模式。这些检查结果会直接显示在 MR 页面上Reviewer 就不需要再去手动看这些细枝末节。第二类是代码格式化检查。Prettier 或 BlackPython这类工具可以让整个团队的代码风格完全统一消除ta 觉得这样缩进好我觉得那样缩进好的低级争论。评审里最浪费时间的就是为格式化问题来回拉锯。3.2 配置评审触发规则工具选型和配置只是第一步。真正让评审流程自动化的是设置一套评审触发规则。我的配置思路如下分支保护规则主干分支main/master禁止直接推送代码。任何合并必须通过 MR/PR且至少一个评审者批准后才能合并。这个规则在 GitLab 的 Settings → Repository → Protected branches 里配置在 GitHub 则是 Settings → Branches → Branch protection rules。强制流水线通过合并要求必须包含流水线成功这个条件。如果 lint 或测试失败合并按钮直接置灰。评审者人数要求根据我们第 2 节的分级标准评审至少需要 1 个人工审批。这个数字在 GitLab 的 Merge request approvals 里设置GitHub 则在 Branch protection rules 里勾选 Require a pull request before merging。配置完这些之后还有一个非常有用的细节在 MR 的模板里引导作者提供评审所需的上下文信息。我用的模板长这样## 需求背景 用两三句话说明这个 MR 要解决什么问题 ## 变更说明 列出主要改动点尤其是跨模块调用和公共函数改动 ## 风险点 哪些改动可能影响现有功能需要重点 review 哪些地方 ## 测试说明 本地测试覆盖了哪些场景有没有补充测试用例 ## 截图 / 录屏可选 UI 相关改动建议贴前后对比这个模板看起来简单但实际效果非常显著。它倒逼作者在提交代码时先想清楚我改了啥为什么要改哪里可能出问题评审者也能在快速了解上下文之后把精力集中在真正值得看的代码上。3.3 自动化检查项的设计自动化检查不是越多越好。如果检查项设置得不合理流水线频繁红灯团队的耐心会迅速耗尽最后变成谁的红灯赶紧改一下强制通过。我见过太多团队死在过度配置上。我的自动化检查项设计原则有三个只检查能稳定判定的问题。例如未使用的变量、缺少依赖声明、格式不符合规范、测试失败、构建失败。凡是会出现误报的检查项宁可不加。检查要快。整个流水线跑超过 10 分钟开发者的等待成本就太高了建议把 lint 和单测放在同一条流水线的前面阶段快速失败。覆盖率阈值要合理。不要盲目要求 100% 覆盖率。我们团队的要求是核心模块覆盖率不低于 80%其余模块不低于 60%。覆盖率检查的目的是防止测试被绕过不是追求数字游戏。还有一类自动化检查经常被忽视那就是 merge 前的分支同步检查。团队里多个人同时开发同一模块时分支容易落后于主干。GitLab 和 GitHub 都支持禁止基于过期分支合并的配置打开这个开关能避免很多合并的时候才发现冲突的问题。3.4 人工评审与自动化检查的分工自动化检查和人工评审不是替代关系而是明确分工机器管规范人管设计。我在团队里反复强调一个原则如果一条评审意见是说这里缩进不对这个变量命名不规范这里有重复代码——这些都应该由机器去查人提这种意见就是在浪费评审时间。反过来如果一条评审意见是说这里的并发处理逻辑有问题如果两个请求同时到达会怎样——这种问题机器查不出来必须人来看。为了落实这个分工我建议在评审流程里加一个Reviewer 只关注以下内容的清单第一条就是不评论任何格式化或风格问题这些由 CI 负责不评论任何可以静态检查出的问题例如未使用变量、明显的空指针这些由 SonarQube 等工具负责。这样做还有一个隐性好处Reviewer 的意见数量会明显下降每一条意见的重量会增加。当团队发现评审留言从这里加个空行那里改个变量名变成这个边界条件你是怎么考虑的这个异常场景要不要加个兜底的时候评审的文化才算真正建立起来了。4. 评审标准与Checklist让凭感觉变成有依据4.1 核心评审维度拆解很多评审之所以无效是因为 Reviewer 没有一个明确的评审框架全程凭感觉看。今天心情好就 Approve明天看到个变量名不顺眼就纠结十分钟。要解决这个问题就必须把评审维度拆开让 Reviewer 对照维度一项项过。我的评审维度分成五个方面正确性这可能是最容易验证但也是最容易被忽略的维度。Reviewer 要看的不只是这段代码能不能运行而是它的输入输出是否符合预期。边界条件测了吗异常分支能走通吗并发情况下会不会冲突与其他模块的交互有没有被破坏可维护性代码读起来费不费劲变量和函数命名是否表达了一致的语义有没有种下将来一定会被误解的种子这里有一个很朴素但很有效的判断标准如果一个月后你自己回来看这段代码能否在五分钟内看懂它是干什么的一致性新代码是否与现有代码风格、架构模式、依赖库选择保持一致有没有出现同一个团队同一个功能两套写法的情况一致性是评审里最容易出现的争议点因为现有写法未必是最佳写法但如果是大规模重构应该走独立的架构评审而不是在一个普通功能 PR 里顺便改掉。安全性这是很多人会忽略的维度。用户的输入有没有做过滤是不是直接拼进了 SQL有没有配置项被硬编码权限校验有没有遗漏做前端的话有没有把敏感信息暴露到客户端基础的安全意识必须在评审中作为固定检查项。可测试性这段代码有没有对应的测试测试覆盖到了关键分支吗如果测试很难写是不是说明代码本身有耦合问题一个简单的判断标准是如果这段代码将来出现问题测试能否第一时间捕捉到4.2 一套可落地的 Review Checklist基于上面的维度我整理了一份适用性比较广的 Review Checklist。它不是一次性完整阅读评审对象而是分轮次逐步深入第一轮 - 快速浏览目标打开 MR 先看改动文件清单和改动行数判断这次改动的主线逻辑。如果一眼看不懂主要意图直接要求作者补充说明不要硬看。第二轮 - 核心逻辑精读从最关键的文件开始逐行阅读重点验证输入输出与边界条件。遇到不理解的逻辑先看有没有对应的测试用例测试有助于理解代码意图。第三轮 - 跨模块影响度检查检查改动的函数或组件的调用方有哪些确认是否影响了这些调用方。这个步骤最容易被忽略但恰恰是线上事故的主要来源。第四轮 - 非功能项快查按照上面的五个维度快速过一遍清单逐个打勾。这份 Checklist 不需要写进文档里让每个人背下来更好的做法是把它做进 MR 模板让 Reviewer 在提交审批时看到## Reviewer 确认 - [ ] 核心逻辑已逐行阅读边界条件与异常分支已验证 - [ ] 跨模块影响已检查确认不会破坏相邻功能 - [ ] 测试覆盖关键场景且测试本身有效 - [ ] 无安全问题输入校验、权限控制、敏感信息 - [ ] 代码符合团队现有架构与技术选型这个办法的巧妙之处在于它通过让 Reviewer 在审批前必须过一遍钩子的方式把评审框架内化成了行为习惯而不是停留在纸面上。4.3 如何在评审中表达不同意见代码评审里最常见的摩擦不是技术分歧而是表达方式导致的情绪冲突。好的评审沟通方式能让整个流程顺畅很多这里分享几个我的原则先说优点再提缺点。哪怕只有一个函数写得漂亮也要先提出来。评审不只是找茬也是正向确认。用提问代替评判。与其说这里写错了改成 XX不如说这个分支如果输入为空会怎样这里我有点没想通。提问给了作者思考的余地也让作者更容易接受反馈。区分必须修改和建议修改。每个评审意见都要明确优先级。如果是 bug 或安全隐患标为必须修改如果是风格偏好或优化建议标为建议。必须修改不修不能合并建议明确说明可以后续处理。不要把个人偏好合理化。比如我觉得用 switch 比 if-else 好这种话基本都是个人偏好除非能讲出具体的可维护性收益或性能差异否则不值得在评审里争论。这些沟通原则看起来是软技巧但实际作用非常大。一个团队如果评审环境让人安全感低所有人都会倾向于写好代码之后不再主动分享评审的价值就彻底归零了。5. 落地过程中踩过的坑与应对方案5.1 评审流程被人为绕过的根因与对策推行评审最大的敌人不是技术问题而是人的问题。最常见的情况是开发者急着上线新功能觉得评审流程碍事于是在群里喊一声我这个功能比较急大家随便看一眼我先合并了有问题后续再改。这种话术一旦被允许评审流程就名存实亡了。应对这个问题的核心不是加强惩罚而是理清紧急性和流程之间的关系。我的做法是在流程上做出制度性保障分支保护规则必须有人工审批通过才能合并这一条这个开关不能被任何开发者关闭包括我自己。提供紧急通道如果真的有线上紧急故障需要立即修复走专门的 hotfix 流程——允许先合并再补评审但只有限定的紧急场景适用且必须在 24 小时内补上评审记录。这个通道的存在让普通开发者没有理由绕开流程。建设团队共识新成员入职的第一周我会明确告诉他们评审不是为了卡你是为了让我对你的代码负责。当团队理解评审是保护机制而非约束机制绕流程的现象会大幅减少。5.2 评审效率低下的根因评审效率低下的一个普遍现象是一个 MR 挂在列表里三四天没人理作者每天催评审者每天说晚点看。出现这种情况根子通常在以下两个地方第一评审者负荷过高。每个人的精力是有限的如果一个核心成员既是多个模块的负责人又是所有 MR 的默认 Reviewer他的评审队列一定会爆。解决方案是给每个模块分配两个以上的后备评审者并在团队里推广小步提交——每次提交的改动量小评审负担就轻Reviewer 自然更愿意及时看。第二评审缺乏明确的响应时限。我们团队有一个不成文的约定标准评审的响应时限是 8 个工作小时内必须给出第一条评论如果 Reviewer 确实没有时间看必须明确回复明天中午前看完而不是已读不回。这个约定看起来简单但对评审节奏的改善非常明显。后来我甚至把它配成了 GitLab 的自动提醒规则——MR 超过 4 小时没有评论自动 对应的 Reviewer 提醒一次。另外一个提升评审效率的技巧是分层评审法第一个 Review先让一个熟悉该模块的同事快速过一遍整体逻辑确认主干设计没有方向性问题第二个 Review再让另一个同事逐行深读。这样避免深读到一个低层错误时才发现上面的主体设计需要推翻的返工。5.3 让坚持评审成为团队习惯的几个实用技巧最后聊聊怎么让评审真正在团队里落地、并且长期坚持。据我观察很多团队不是没推行过评审而是推行一段时间后流程逐渐松弛、形同虚设。这里分享几个亲测有效的小技巧第一把评审纳入绩效考核。不是考核评审意见的数量而是考核评审的参与度——是否按时完成分配的评审任务、是否在评审中给予了实质性的反馈。这一条在团队初期非常重要因为流程还没有形成惯性需要外力推一把。第二定期做评审回顾。每个迭代结束后花半个小时看一下哪些 MR 的评审时间超过了 24 小时哪些模块的评审意见质量最高有没有因为评审没看仔细导致的线上问题这个复盘动作能持续暴露流程中的问题防止流程僵化。第三让评审成为新人培养机制。我会刻意安排初级开发者和资深开发者互为评审者。资深者给初级者提供代码与架构指导初级者则能在资深者的代码中发现测试遗漏或文档缺失等问题这也是很好的成长机会。第四以身作则。作为团队的技术负责人我自己的每一个 MR 都会认真准备描述和测试说明评审意见全部按时响应。这不是形式主义而是向团队传递一个信号评审是所有人的事不是对别人的要求。就我自己的体会来说代码评审推行得越久越能感受到它的真正回报不在于从代码里找出几个错误而在于它塑造的讨论氛围和知识共享机制。它强迫每个人都为别人的代码停下来想一想被迫去读别人的设计思路这其实是团队技术成长最快的途径。工具、规则、checklist 都是辅助真正让 open-code-review 运转起来的是团队里每个参与者都愿意认真对待别人代码的那份责任心。
返回列表