资讯动态

开放代码评审方案:用自动化与质量门禁打造高效Code Review工作流

发布时间:2026/10/19 9:49:42 来源:尧图企业网站定制
提到代码审查我一直有一个执念所有需要重复执行、且标准明确的事情都应该交给机器而人的精力应该花在讨论设计、权衡取舍这些机器代替不了的事情上。这也是我发起 open-code-review 这套开放代码评审方案的初衷。它不是某个公司内部的秘密工具而是一套基于开源生态、可复制、可裁剪的代码审查工作流参考实现核心解决三个问题评审标准不统一、评审过程太依赖个人经验、以及合并前的质量门禁形同虚设。如果你正在带一个5到50人的研发团队或者你想给自己的开源项目加上一层自动化保障这篇文章非常适合你。我会从设计思路、工具选型、具体搭建、人工评审模板、质量门禁、以及我实际踩过的坑这几个维度展开尽量把能直接复用的配置和脚本都放出来。内容偏实战建议你打开编辑器跟着做一遍效果会比你纯收藏好得多。1. 为什么需要一套开放的代码审查方案1.1 代码审查在现实中常常变成形式主义每个团队嘴上都说要做 Code Review但真正落地后往往变成这样开发者在周五下午一口气提了五个 PR评审人看着满屏的 diff快速滑动滚动条然后留下几个无关痛痒的评论——“这里命名可以改一下”“建议补个注释”最后点一下 Approve。整个过程看起来走完了流程实际上没有拦截住任何风险。问题的根源不是团队不重视质量而是缺少一套明确、可执行、能沉淀的评审标准。人的注意力是有限的当代码规模变大、PR 越来越多时评审人很难每次都从零开始检查“是否遗漏了测试”“是否遵循了团队规范”“是否有安全性隐患”。这些重复性工作本身就应该被自动化把人的精力释放出来去解决真正的设计问题。我在多个团队里做过测试一个很典型的指标是没有自动化门禁时静态问题ESLint 报错、未使用的依赖、格式不统一平均要经过 2-3 轮人工评论才能发现而接入了自动化检查后这些问题在 PR 提交的第一时间就被机器人指出人工评审只看逻辑和架构。这两种体验的差距是跨时代的。1.2 open-code-review 想解决的问题open-code-review 这个名字里有两个关键词一个是“open”一个是“review”。所谓“open”指的是整套规则、脚本、模板全部开放任何人都可以看见、可以修改、可以贡献。团队的代码规范不再散落在 Wiki 里而是以代码的形式存在于仓库中Diff 和 Review 都可以针对规则本身进行。另一个关键词“review”则强调所有机制都围绕评审效率展开。具体来说这个方案要解决四个问题规范检查自动化代码风格、基础语法、复杂度、重复率等交给 ESLint、Stylelint、SonarQube 处理PR 提交时自动运行结果直接以评论形式反馈到对话中。评审过程可量化合并请求的大小、改动文件数量、涉及模块范围用 Danger JS 生成结构化数据让评审人一眼看清风险面。人工评审有抓手提供统一的 Checklist 模板和意见分类法拉齐团队认知减少“我觉得这里不好”这种模糊表达。质量门禁不强拆通过分支保护策略让自动化检查结果成为合并的前置条件避免“红灯也能强行合并”。这套方案适合的团队画像很清晰代码托管在 GitHub或者支持 GitHub Actions 的自建服务团队规模 5 人以上目前评审标准主要靠个人自觉。如果你已经引入了 GitLab CI 或其他 CI 系统思路完全一致只需要把 Action 换成对应的 Pipeline 脚本即可。2. 整体架构与核心设计思路2.1 基于开源自建不重复造轮子在技术选型时我纠结过一个问题到底是自己写一个评审机器人还是把已有开源工具编排起来。最后我选择了后者。原因很简单评审链路的每个环节都有成熟的工具自己造轮子既费时又难维护。open-code-review 依赖的核心组件如下组件作用为什么选它GitHub Actions自动化执行入口与仓库深度集成免费额度够用生态成熟Danger JS评审规则引擎可用 TypeScript 写规则灵活控制评论内容ESLint Stylelint静态代码规范社区规则丰富可自定义插件SonarQube深度质量分析检测重复率、复杂度、潜在 bugcommitlint提交信息规范统一 commit 格式为自动生成 changelog 做准备reviewdog检查结果上屏把 lint 结果逐条评论到 diff 对应行体验很好这套组合的特点是职责单一、松耦合。每个工具都可以单独替换比如你用的是 Java 项目可以把 ESLint 换成 Checkstyle如果你不想用 SonarQube也可以只保留 Danger 规则。开放的意义就在于此你可以把 open-code-review 当成一个起点而不是一个绑定死的框架。2.2 open-code-review 的目录结构与工作流一个典型的 open-code-review 仓库目录结构是这样的open-code-review/ ├── .github/ │ ├── workflows/ │ │ ├── pr-checks.yml │ │ └── sonarqube-scan.yml │ └── PULL_REQUEST_TEMPLATE.md ├── danger/ │ ├── dangerfile.ts │ └── rules/ │ ├── pr-size.ts │ ├── dependency-check.ts │ └── docs-check.ts ├── configs/ │ ├── .eslintrc.js │ ├── .stylelintrc.js │ └── commitlint.config.js ├── scripts/ │ ├── review-summary.ts │ └── parse-result.ts └── docs/ └── review-checklist.md工作流按 PR 的整个生命周期来设计开发者提交 PR 后GitHub Actions 会并行启动多个 Job第一路跑 Danger JS负责判断 PR 的元信息第二路跑 ESLint、Stylelint负责静态规范第三路触发 SonarQube 扫描处理深度质量分析。所有结果会汇聚成一个 Markdown 评论发布到 PR 页面。这样人工评审人打开 PR 时第一眼看到的是机器人整理好的风险摘要而不是一份冷冰冰的 diff。这其实解决了一个很重要的问题信息过载。一个改动 1000 行的 PR让评审人自己看 diff他很难捕捉到所有异常点。但自动化的结果能帮他划好重点比如“这个 PR 改动了 12 个文件其中 8 个是样式文件3 个是接口定义风险集中在 xx 模块”。有了这个摘要评审人再切入代码时效率会提升非常多。3. 从零搭建open-code-review实操步骤3.1 初始化项目与基础配置首先创建一个普通仓库然后克隆到本地执行 npm 初始化mkdir open-code-review cd open-code-review git init npm init -y接下来安装基础依赖Danger JS 是核心的评审规则引擎需要作为 devDependency 安装npm install --save-dev danger typescript types/node npm install --save-dev eslint typescript-eslint/parser typescript-eslint/eslint-plugin npm install --save-dev stylelint stylelint-config-standard npm install --save-dev commitlint/cli commitlint/config-conventional建议把 Node 版本固定在 18 或 20避免因版本差异导致 Action 运行环境不一致在仓库根目录添加.nvmrc18.18.0然后在package.json中增加脚本{ scripts: { danger: danger ci, lint: eslint . --ext .ts,.tsx,.js,.jsx, lint:style: stylelint \**/*.{css,scss}\, commitlint: commitlint --edit } }3.2 编写Dangerfile定义自动化评审规则Dangerfile 是 open-code-review 的核心。它本质上是一个 TypeScript 文件在 CI 环境中执行通过danger提供的内置对象访问 PR 信息。我建议把规则拆成独立文件用import引入避免 Dangerfile 变成一团乱麻。我先展示一个最小可用的danger/dangerfile.tsimport { danger, warn, fail, message, markdown } from danger; import { checkPrSize } from ./rules/pr-size; import { trackDependencyChanges } from ./rules/dependency-check; export default async () { const pr danger.github.pr; const changedFiles danger.git.modified_files.concat(danger.git.created_files); message(该 PR 一共变更了 **${changedFiles.length}** 个文件实际有效行数变化请参考 diff 数据。); // 检查 PR 规模 checkPrSize(pr, changedFiles); // 检查依赖变更 trackDependencyChanges(danger.git.modified_files); // 检查是否包含测试文件 const hasTests changedFiles.some(f f.includes(.test.) || f.includes(.spec.) || f.includes(__tests__)); if (!hasTests changedFiles.length 5) { warn(这是一个改动量较大的 PR但未发现对应的测试文件请补充测试或说明原因。); } // 如果修改了 src 下的代码要求必须更新相关文档 const srcChanged changedFiles.filter(f f.startsWith(src/)); const docsChanged changedFiles.some(f f.startsWith(docs/) || f.includes(README)); if (srcChanged.length 3 !docsChanged) { warn(你修改了多个源文件建议同步更新 README 或接口文档。); } };为了演示规则拆分danger/rules/pr-size.ts是这样的import { GitHubPRDSL as PR } from danger; export function checkPrSize(pr: any, changedFiles: string[]) { const additions pr.additions ?? 0; const deletions pr.deletions ?? 0; const total additions deletions; if (total 1000) { fail(PR 变更行数为 ${total} 行超过 1000 行。请拆分为更小的 PR方便评审。); } else if (total 500) { warn(PR 变更行数为 ${total} 行超过 500 行。建议拆分为多个逻辑提交。); } if (changedFiles.length 20) { fail(PR 变更文件数为 ${changedFiles.length} 个超过 20 个。请检查是否合入了不必要的文件。); } }你可能会问为什么要把规模作为强制项因为代码评审的最佳实践是“小步快跑”。一个 500 行的 PR 和 5000 行的 PR评审人的错误捕捉率完全不同。我们曾在团队里做过统计超过 1000 行的 PR评审意见被有效采纳的比例只有 30% 多而小于 300 行的 PR意见采纳率接近 70%。所以把规模限制作为 fail 条件是对质量负责。3.3 接入ESLint与Stylelint实现规范检查ESLint 的作用是统一代码风格和捕捉低级错误。open-code-review 不强制使用特定规则集但建议从eslint:recommended和typescript-eslint/recommended开始再按团队习惯收敛。这里给一份基础配置configs/.eslintrc.jsmodule.exports { root: true, parser: typescript-eslint/parser, plugins: [typescript-eslint], extends: [eslint:recommended, plugin:typescript-eslint/recommended, prettier], env: { node: true, es2022: true, browser: true, }, rules: { typescript-eslint/no-explicit-any: warn, no-console: process.env.NODE_ENV production ? [error, { allow: [warn, error] }] : off, max-lines: [warn, 300], complexity: [error, 10], }, };注意这里我把complexity设置成了 error复杂度超过 10 直接算失败。有些同学会觉得太严格实际上这个阈值可以按团队水平调整。我见过很多线上 bug 都是因为函数分支太多、状态机混乱导致的复杂度检查真的能提前拦下来。Stylelint 配置类似主要针对 CSS/SCSS 文件module.exports { extends: stylelint-config-standard, rules: { color-no-invalid-hex: true, block-no-empty: true, max-nesting-depth: 2, }, };要让这些检查结果出现在 PR 评论里而不是只显示在 CI 日志中我们需要用 reviewdog 这类工具来“翻译”结果。在pr-checks.yml工作流中一个典型的步骤是这样的- name: Run ESLint with reviewdog uses: reviewdog/action-eslintv1 with: github_token: ${{ secrets.GITHUB_TOKEN }} reporter: github-pr-review eslint_flags: configs/.eslintrc.js level: warningreporter: github-pr-review会让每条 lint 错误以评论的形式出现在对应代码行这种体验对开发者非常友好不用切出 PR 页面就能直接修复。3.4 使用commitlint规范提交信息提交信息不规范后果不会立刻显现但当你需要生成 changelog 或者用 git blame 追溯某段代码时就会非常痛苦。我见过有些团队的 commit message 是 “update”“fix bug”“1”完全没法用。commitlint 的配置放在configs/commitlint.config.jsmodule.exports { extends: [commitlint/config-conventional], rules: { type-enum: [2, always, [feat, fix, docs, style, refactor, test, chore, perf, revert]], subject-case: [0], }, };为了让本地提交时就直接拦截配合 Husky 使用。安装npm install --save-dev husky npx husky init在生成的.husky/commit-msg中写入npx --no -- commitlint --edit $1对于已经在错误的提交历史上迭代的存量项目不建议强制全量整改 commit 信息可以只对新提交生效。我的经验是从某个迭代版本开始所有新提交必须遵循规范历史问题等重写或合并时再统一处理。4. 人工评审流程与模板设计4.1 设计评审Checklist把经验固化成模板自动化能解决规范问题但解决不了架构问题。为了让人工评审不空转我设计了一份 Checklist放在.github/PULL_REQUEST_TEMPLATE.md里每个 PR 提交时都会被自动带入描述区。模板如下## 变更说明 简述本次变更解决的问题和主要实现方式 ## 自查清单 - [ ] 是否补充/更新了单元测试 - [ ] 是否考虑边界条件和异常输入 - [ ] 是否涉及依赖变更如果是是否同步更新了 lock 文件 - [ ] 是否有明显的性能风险循环嵌套、大对象拷贝等 - [ ] 是否有安全性问题SQL 注入、XSS、权限绕过等 - [ ] 是否更新了接口文档或 README - [ ] 是否包含调试代码或临时代码 ## 测试计划 列出本地测试步骤与预期结果 ## 评审人注意事项 如有需要重点review的模块请在此说明这个模板的价值在于让开发者在提交 PR 之前先自查一遍很大一部分低级问题在源头就被消灭了。我们团队在引入模板两个月后PR 的“缺测试”评论数量下降了 54%。4.2 评审意见的erfa分类法评审意见的表达方式直接影响沟通效率。我推荐团队内部使用 ERFA 分类法EError必须修复的错误、RRisk存在风险需要确认、FFault不符合约定但不是错、AAdvice非强制建议。这样分类有什么好处评审人不再需要长篇大论解释“为什么这个问题严重”只需在评论前加上缩写。开发者也能快速判断优先级E 类必须等下轮审核通过R 类需要回复是否接受风险F 和 A 可以选择性处理。在 Dangerfile 里面我预留了一个解析 ERFA 标记的工具函数后续可以统计每个模块的 E/R 类问题数量。虽然 Danger 不会自动理解人类评论但把这个格式写在团队规范里配合 GitHub 的 label 或 review thread能形成很好的工作习惯。4.3 与自动化结果合并机器人评论的结构机器人评论不是越多越好如果每个工具都发一条PR 页面会非常吵。open-code-review 的做法是把所有结果聚合到一条评论里按区块展示。Dangerfile 里使用markdown函数可以拼接自定义内容。我们设计一个scripts/review-summary.ts在 Dangerfile 中调用export function renderSummary(checks: CheckResult[]) { const tableRows checks .map(c | ${c.name} | ${c.status} | ${c.detail || -} |) .join(\n); return ### 自动化检查摘要 | 检查项 | 状态 | 说明 | | --- | --- | --- | ${tableRows} ; }最终展示在 PR 评论里的效果如下检查项状态说明PR 规模⚠️ Warning变更行数 560 行建议拆分测试文件❌ Fail未检测到测试文件ESLint✅ Pass0 error / 2 warningStylelint✅ Pass0 error / 0 warning依赖变化✅ Pass无依赖变更提交信息✅ Passcommitlint 校验通过代码评审人只需要看这一张表就能决定接下来要深入看哪个文件。是不是觉得比打开十几个 tab 去查各个 CI 日志要舒服多了5. 质量门禁与数据看板5.1 用GitHub Branch Protection设置合并门槛自动化检查跑出结果只是第一步关键是强制遵守。GitHub 分支保护规则可以设置为“必须要求 PR 通过检查才能合并”。步骤如下打开仓库 Settings - Branches - Add rule。在 Branch name pattern 中输入main或master。勾选 Require a pull request before merging。勾选 Require status checks to pass before merging。在搜索框中勾选我们工作流里的 Job 名称比如check和sonarqube。勾选 Require conversation resolution。保存规则。这里有个小细节如果你在 workflow 中把 Job 命名为checkGitHub 会自动识别为 “check” 状态。如果希望只允许管理员合并时绕过也可以勾选 Include administrators但我不建议这么做因为一旦给管理员开绿色通道规则就会很快形同虚设。5.2 统计审查数据的轻量方案质量数据不是用来向领导汇报的而是用来发现团队问题的。open-code-review 没有把统计功能做成重型模块而是借助 GitHub 自带的 Insights 数据和 Danger 产出的日志。你可以在 PR 合并后把审查统计追加到一个仓库内的 JSON 文件比如.review-stats/statistics.json{ date: 2024-01-15, pr: 123, changedFiles: 8, totalLines: 540, botComments: 3, humanComments: 5, erfaE: 1, erfaR: 2 }然后用一个简单的定时任务或者本地脚本聚合这些数据。当数据积累到一定量级时你会看到几个趋势某位开发者的 PR 反复出现同一类 E 级问题说明他对这个规范理解不到位需要针对性培训。某个模块的 R 级问题比例特别高说明这块业务复杂度超出现有文档覆盖范围需要重构或补充设计。机器人能拦截的问题比例越高说明人工的重复劳动越少。6. 常见问题与踩坑记录6.1 Danger JS在本地跑不过Danger 在本地运行需要设置环境变量DANGER_GITHUB_API_TOKEN很多人在本地执行npm run danger时直接报了权限错误。解决方法是申请一个只读权限的 GitHub token然后导出到 shellexport DANGER_GITHUB_API_TOKENghp_xxx npm run danger注意这个 token 不要提交到仓库里否则会被 GitHub 安全服务扫描并自动失效。如果你只是想在本地跑语法检查可以加--dangerfile参数指定文件但最终效果还是要以 CI 为准。6.2 reviewdog何时用github-pr-review何时用github-check我最初把 ESLint 的 reporter 设置为github-pr-review后来发现每次 push 新 commit 时reviewdog 会清掉旧评论重新发布导致其他已经在讨论中的评论位置发生跳动。如果团队经常在 PR 里保持长对话建议对 lint 结果使用github-check它会展示在 Checks 标签页而不是正文评论区。对于 Danger 的摘要评论则保留在评论区因为它本身会更新同一条 comment不会刷屏。6.3 高并发PR导致的Action排队当团队同时提交很多 PR 时免费版 GitHub Actions 的并发限制会让检查任务排队导致合并等待时间变长。我的建议是给 workflow 加上concurrency配置相同 PR 的新提交自动取消旧任务concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true这个配置能显著减少无效的 CI 时间。注意它同样适用于 push 到 main 的流水线group 需要区分分支避免误取消。6.4 安全注意事项在 Action 中使用GITHUB_TOKEN时一定要使用最小权限。进入仓库 Settings - Actions - General把 Workflow permissions 改成 “Read repository contents and packages permissions”然后单独给需要的 Job 创建一个 Secret。不要让 workflow 中的脚本直接把 token 打印到日志里也永远不要用pull_request_target触发运行时才拉取 untrusted code 的 job这会带来严重的安全风险。7. 真实使用效果与经验总结open-code-review 在我们团队落地已经超过半年一些真实感受想分享给你。数据上的变化是明显的PR 平均评审时间从原来的 8.6 小时缩短到 2.1 小时静态问题流出率下降了 60% 以上测试覆盖率提升了约 12 个百分点。但比这些数字更重要的是团队对 Code Review 这件事的态度发生了改变。以前大家觉得评审是“挑刺”是“卡进度”现在大家发现机器人把技术债和规范问题都先清了一遍人工评审终于可以聚焦在“这个方案有没有问题”“这个接口设计是否合理”这种真正有价值的事情上。我们有一个新同学入职后用 open-code-review 的规则文件当作学习材料很快就能理解团队的编码偏好这是最让我意外的一个收获。最后再给你一个建议不要一上来就把所有规则调到最严格分三步走。第一步先接入自动化检查把最明显的错误拦下来第二步引入 Danger 规则规范 PR 尺寸和文档要求第三步再强化分支保护让不合规的 PR 彻底无法合并。这个过程大概需要两到三个迭代周期团队的接受度会高很多。代码评审本身是一场对抗熵增的持久战工具只是把“认真”这件事变得更容易做到。希望 open-code-review 能帮你走完关键的第一步。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑