先说一件让我下决心换工具的事。上个月我们有个支付状态更新的接口出了事故,根因是新增代码里把一个布尔判断写反了。代码过了人工 Review,也过了单元测试,但没有任何一层拦住它。我翻了一下当天那条 PR,评审意见里只有一句 "LGTM",外加两条关于缩进的格式建议。那一刻我意识到,团队不是不重视 Code Review,而是把最值钱的注意力浪费在了最不该由人来盯的事情上。
后来我开始把open-code-review这套开源审查工具链接入团队的日常流程。原本以为它只是又一个 lint 工具,真正跑起来之后才发现,它解决的是另一个层级的问题:让评审行为本身变得透明、可扩展、可度量。这篇文章我会从设计思路、核心机制、落地配置、自定义规则到实战中的坑,完整拆一遍我在这套工具上的使用经验。如果你正觉得 Code Review 在团队里流于形式,或者想找一个能真正沉淀成团队资产的开源方案,这篇应该对你有用。
1. 为什么我要把开源工具抬进 Code Review 流程
1.1 传统评审里最消耗人的三个环节
先说传统 Code Review 的真实状态。多数团队不是没有流程,Code Review 也确实是合并代码前的必经关卡,但问题恰恰出在"必经"这两个字上。评审人每天打开 PR 列表,面对的是几百行甚至上千行 diff,真正能分配给人脑的深度注意力是有限的。
第一个消耗点是重复低级问题的识别。新增代码里带了个console.log、某个常量写死了魔法数字、异常被 catch 后直接吞掉、文件权限没处理好……这些错误不是技术难度高,而是出现频率太高。评审人每次都要在 diff 里肉眼扫一遍,扫完这条 PR 再扫下一条,一天下来注意力被严重稀释。等真遇到一个复杂的设计决策问题,人已经看不动了。
第二个消耗点是评审意见表达不清晰。我在团队里看到过太多类似"这里不太对""建议优化一下"的评语。作者收到这种意见,第一反应不是去改代码,而是猜评审人到底在说什么。来回一轮就是几十分钟,时间全消耗在沟通成本上,而不是问题本身。
第三个消耗点是评审结果无法沉淀。评审意见散落在各个 PR 页面里,既不能被检索,也不能转化为后续开发的行为约束。同一类问题这周在 A 项目里被提,下周在 B 项目里又出现,等于团队在反复交同一笔学费。
这三个环节消耗的不是某一个人,而是整个研发组织的带宽。我当时就萌生了一个想法:能不能把这些可被规则化的判断全部交给工具,让人的精力只保留在"需要人来判断"的部分上。
1.2 open-code-review 不是 linter 的替代品
接触 open-code-review 之前,很多人会问:它和 ESLint、SonarQube 有什么区别?我的理解是,传统 linter 做的是"对已有代码做静态检查",而 open-code-review 的核心对象不是文件,是变更本身。
它关心的是"这次提交改了什么、改得对不对、影响面有多大"。同样是检查一行代码,linter 看的是它是否符合语法和风格规范,open-code-review 看的是它在一个 diff 上下文里是否存在风险。比如你往一个公共函数里加了一个参数,linter 大概率不会说什么,但如果这个函数被几十个模块引用,open-code-review 会结合调用链和变更范围给出更高风险评级,这个维度的提醒是普通 lint 工具给不了的。
它的名字里有一个关键词:open。在项目设计上它至少有三层开放:
- 规则开放:审查规则不是内置写死的,团队可以按自己的技术栈和业务特点写插件。
- 流程开放:命令行工具、CI 集成、报告格式都是透明可改的,能嵌进 GitHub、GitLab 或者任意自研平台。
- 数据开放:每一次审查产生的 stdout、JSON、Markdown 报告都能被采集,用来度量团队代码质量趋势。
我当时看重它,正是因为它在"自动化能力"和"人的主导权"之间留了足够大的空间。工具负责体力活,人负责决策,而不是相反。
2. diff 如何变成一份可执行的审查清单
2.1 一条审查流水线的五个关键阶段
要理解 open-code-review 为什么能跨语言、跨框架运行,就要拆解它的流水线设计。我见过很多人以为这类工具是直接拿正则去匹配 diff 文本,其实真正可用的审查工具不会那么粗暴。一条 diff 从进入工具到最后输出报告,至少要经过五个阶段。
第一阶段是变更获取。工具通过git diff拿到变更内容,同时记录变更类型是新增、删除还是修改。这个信息很关键,默认情况下 open-code-review 只审查新增和修改的行,因为存量代码的问题应该通过专项重构去解决,而不是卡在每一条 PR 上。
第二阶段是语言识别。工具会根据文件扩展名和内容特征判断文件属于哪类语言,再选择合适的解析器。同一个 diff 里有 JavaScript、Python、YAML、Dockerfile 非常正常,不按语言分流的话,规则很快会被误报淹没。
第三阶段是结构解析。这里会做语法层面的解析,识别出函数、类、调用、变量等代码结构。解析深度不是一次全量 AST,而是按需增量构建,这个我后面单独说。
第四阶段是规则匹配。规则引擎把解析后的结构输入一组规则,每一条规则输出命中结果和严重级别。规则之间通过 id 去重,避免同样的位置被重复报告。
最后一个阶段是报告生成。所有结果按文件路径和行号聚合,输出成表格、JSON 或 Markdown。如果接了 CI,这里还会按照门禁策略决定是阻塞合并还是只做提醒。
五个阶段加在一起,用户感知到的就是一条命令:ocr review。但这条命令背后对应的是完整的数据管线,任何一个环节做糙了,最终的审查质量都会塌。
2.2 解析层为什么选择"增量分析"而不是全量 AST
说到解析层,很多人第一直觉是:要准确理解代码,岂不是要把整个仓库的 AST 都建出来?理论上是的,但实践上不行。一个中等规模的 monorepo,全量 AST 解析可能要几十秒甚至几分钟,这在 CI 场景下是不可接受的。
open-code-review 走的路线是增量分析:只对变更涉及的文件和函数做结构解析,其他依赖部分用轻量索引替代。它先通过 git diff 定位到变更文件和变更的函数范围,然后只对这部分做全量语法解析,对于被引用的外部模块则使用符号表查询。这个设计和现代编译器的增量编译思路很像,核心取舍是:在保证审查足够准确的前提下,把耗时控制在一个用户能接受的范围内。
举个例子,一个修改只涉及UserService.js里的getUserById函数,工具不会重新解析整个项目,而是只解析这个函数体,同时收集它调用了哪些其他模块的方法,再对这些调用做浅层校验。这样处理的好处很明显:小 diff 毫秒级完成,大 diff 秒级完成,不会因为仓库变大而让单次审查时间线性膨胀。
当然,增量分析也有代价。某些需要跨文件上下文才能确认的问题,它只能给出"疑似"级别的提示,而不是铁板钉钉的结论。这个设计我愿意接受,因为在代码审查里,"快速发现问题"比"追求完美理解"更重要,剩下的模糊地带正好交给人来处理。
2.3 规则引擎的三段式匹配模型
open-code-review 的规则引擎设计得比较规整,我自己在做插件时体会特别深。所有规则都遵循一个"三段式"匹配模型,分别叫前置条件、匹配主体和上下文校验。
前置条件决定"这条规则要不要在当前文件上运行"。比如你写了一条银行账号格式的规则,它只应该跑在 Java 服务端代码上,不应该跑在前端 CSS 文件上。前置条件里声明文件类型、变更类型、目录范围,引擎会在进入解析前先把不符合条件的规则过滤掉,省掉了大量无效计算。
匹配主体决定"命中一个什么样的问题"。这里支持两种方式:一种是基于 AST 的结构匹配,适合"函数内不允许 catch 后什么都不写"这类精确问题;另一种是基于文本模式的高速匹配,适合"不允许新增 console.log"这类一眼就能看出的问题。结构匹配准但慢,文本匹配快但糙,规则作者需要自己权衡。
上下文校验决定"这个命中是否真的算一个问题"。这是防止误报最关键的环节。比如你检测到代码里新增了一个 setTimeout,可是如果它出现在某个防抖函数内部,可能就不是问题。上下文校验允许规则作者读取函数签名、调用链、注释、周边代码结构,再决定要不要上报。
这个三段式模型的价值在于,它把规则从"正则脚本"升级成了"小型程序"。规则不再是碰运气式的文本搜索,而是有明确能力和边界的逻辑单元。
3. 从安装到第一份报告:我建议的 15 分钟上手路径
3.1 环境准备与安装方式
如果你也想在本地快速试一下 open-code-review,我先说一下环境要求。整体来说前置依赖很少,不需要额外装数据库或独立服务,只要机器上有 Node.js 的运行环境就可以。官方文档建议 Node.js 18 及以上版本,我实际测试下来 20 版本最稳。
安装方式取决于你怎么用。如果只是个人在项目里快速验证,直接全局安装命令行工具最方便:
npm install -g @open-code-review/cli如果你更希望把它锁在项目依赖里,让每个 clone 项目的人拿到一致版本,那么在项目根目录下做局部安装更合适:
npm install --save-dev @open-code-review/cli我个人的习惯是两种都用:本机全局装一个,方便随手跑临时目录;项目里再局部装一个,保证 CI 和本地行为完全一致。安装完可以跑一下版本号确认成功:
ocr --version命令行工具的名字就是ocr,注意别和光学字符识别那个概念搞混,团队内部叫顺口了倒也没啥问题。
3.2 初始化配置:别急着改规则
装完工具之后第一件事不是写规则,而是初始化配置。open-code-review 会读取项目根目录下的.open-code-review.yaml文件,没有这个文件时使用内置默认配置。默认配置其实已经能用,它内置了一套通用的规则集,覆盖了很多团队常见的低级问题。
我建议初始化时直接生成一个显式配置文件,方便后续版本管理:
ocr init这条命令会在当前目录生成一个最小可用配置,内容大致如下:
version: 1 rules: no-new-console: severity: warning no-magic-number: severity: warning options: ignore: [0, 1, 100] ignore_paths: - dist/** - build/** - "**/*.min.js" baseline: .ocr-baseline.json我踩过的第一个坑就是拿到配置后急着去改规则。实际上,刚接入的时候最重要的不是你主动添加多少规则,而是先看默认规则在你的项目里会误报多少。所以我会把配置文件里自己能看懂的先留着,其他规则一律先保持默认,跑完第一轮审查再根据结果调整。
配置里baseline这个字段很关键。它存储的是"老代码里已经存在的问题快照"。有了它,工具在后续审查时会自动把存量问题和新增问题区分开。这个机制我建议第一天接入就必须打开,否则大量历史遗留问题会瞬间淹没你的 CI 输出,团队一看满屏报错就直接关掉工具了。
3.3 跑本地审查并读懂报告
初始化完成之后,就可以跑第一轮审查了。审查方式有两种:一种是直接审查暂存区里尚未提交的变更,适合提交代码前自己检查:
ocr review --staged另一种是指定一个 git 范围,比如审查最近一次提交:
ocr review --range HEAD~1..HEAD如果没有任何参数,工具默认会从 git 的未提交变更里拿 diff。我第一次跑的是--staged模式,输出结果是一个表格,按照文件路径分组展示:
| 级别 | 文件 | 行号 | 规则 | 描述 |
|---|---|---|---|---|
| error | src/payment/status.ts | 42 | no-unhandled-catch | catch 块里没有任何处理逻辑,异常被静默吞掉 |
| warning | src/payment/status.ts | 47 | no-magic-number | 新增代码出现魔法数字 86400,建议提取为常量 |
| warning | src/utils/logger.ts | 18 | no-new-console | 不建议在生产代码中直接使用 console.log |
我第一次跑完其实挺惊讶的,一个看起来只有三行的修改,工具揪出了一个 error 和两个 warning。那个 error 正好是"吞异常"的问题,人工评审很容易忽略,因为 catch 块里确实没有报错,看起来人畜无害,但出了问题根本无从排查。
报告里每一行都会精确到规则 id,方便你回查规则定义。如果你想把报告归档或接其他平台,可以用--format json输出完整结构化数据,这个字段我后面讲度量时会用到。
跑完这一轮,你对工具的工作方式就有一个完整感知了:拿到 diff、解析结构、匹配规则、输出报告。接下来才轮到自己动手定制。
4. 写一条自定义规则需要知道的全部细节
4.1 规则对象的接口约定
open-code-review 的自定义规则是一段 JavaScript 模块,导出固定结构的对象。核心字段有四个:id是规则唯一标识,不能和内置规则重名;severity决定问题级别,可选error、warning、info;match声明该规则在什么条件下激活;check是真正执行检查的函数。
基础模板大致长这样:
module.exports = { id: 'my-first-rule', severity: 'warning', meta: { title: '我的第一条规则', description: '用一句话描述这条规则检查什么' }, match(ctx) { return ctx.file.ext === '.js' && ctx.kind === 'added'; }, check(ctx) { const issues = []; // 这里写检查逻辑 return issues; } };match阶段返回布尔值,决定了是否进入check。这个设计很有用,因为一个仓库里往往存在各种类型的文件,你完全可以在match阶段把所有不相关的规则过滤掉,避免check做大量无用计算。
4.2 实战:禁止新增魔法数字
我拿"禁止魔法数字"这条规则来完整走一遍。很多团队希望新代码里不出现难以理解的常量,但直接改内置规则配置不够灵活,因为在check阶段你需要拿到 AST 节点才能准确判断。
下面是我在实际项目里写过的简化版本:
module.exports = { id: 'team/no-magic-number', severity: 'warning', meta: { title: '禁止新增魔法数字', description: '新增代码中不允许直接出现裸数字,除非在 ignore 列表中' }, match(ctx) { return ['.js', '.ts', '.jsx', '.tsx'].includes(ctx.file.ext) && ctx.kind === 'added'; }, check(ctx) { const issues = []; const ignore = new Set(['0', '1', '2', '100', '-1']); ctx.ast.walk(node => { if (node.type === 'NumericLiteral') { const value = String(node.value); if (ignore.has(value)) return; issues.push({ line: node.loc.start.line, message: `魔法数字 ${value} 建议提取为具名常量` }); } }); return issues; } };这里用到了ctx.ast.walk来遍历 AST 中的所有节点。NumericLiteral节点表示数字字面量。node.loc.start.line拿到的是行号,报告会精确指向它。
有几个细节非常影响体验。第一,ignore 列表要尽早配置好,否则像0、1、-1这种正常代码里大量出现的数字会被反复误报,团队会产生噪感。第二,魔法数字检测不是越严格越好,布尔状态、超时时间、端口号这些场景需要单独讨论。我们团队最终采用的方式是抓大放小:只对三行以内的表达式里的裸数字做提示,超长表达式不处理,避免误伤业务逻辑。
4.3 实战:高危函数调用检测
另一类非常实用的自定义规则是检查高危 API 调用。比如团队规定生产代码不允许使用eval,不允许动态拼接 shell 命令,严格模式下还不允许直接调用某些已经废弃的函数。
实现原理和魔法数字类似,区别在于你要命中的是函数调用节点。下面这段代码检测新增代码中是否有eval调用:
module.exports = { id: 'team/no-eval', severity: 'error', meta: { title: '禁止使用 eval', description: '安全与性能原因,生产代码不允许使用 eval' }, match(ctx) { return ['.js', '.ts'].includes(ctx.file.ext) && ctx.kind === 'added'; }, check(ctx) { const issues = []; ctx.ast.walk(node => { if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === 'eval') { issues.push({ line: node.loc.start.line, message: '禁止使用 eval,请使用 JSON.parse 或替代方案' }); } }); return issues; } };这条规则的匹配点在于CallExpression节点,node.callee.name === 'eval'表示调用名就叫eval。如果想匹配对象方法调用,比如shell.exec('rm -rf'),那就要看node.callee.property.name。这类规则的价值在于,它把团队在安全评审中反复强调的底线,固化成机器可执行的检查,不再靠人每次口头提醒。
高频高危调用很适合沉淀成一个规则集:eval、Function构造函数、child_process.exec、innerHTML赋值,每一条都可以单独配置。新同学合代码时如果触发了这类规则,工具直接报 error 并给出解决方案提示,比评审人写长篇评论高效得多。
4.4 规则的优先级与测试
自定义规则写出来之后,不能直接扔进生产库,至少要跑一轮本地测试。open-code-review 提供了一套简单的规则测试机制,你可以在测试文件里构造一小段代码,然后断言规则是否按预期命中。
一个最小的测试用例长这样:
const { runRule } = require('@open-code-review/test-utils'); const rule = require('../rules/team/no-magic-number'); it('应该命中裸数字', () => { const code = 'const timeout = 3000;'; const result = runRule(rule, { code, file: 'demo.ts', kind: 'added' }); expect(result.issues.length).toBe(1); });我强烈建议每条规则至少配三个测试用例:一个命中场景、一个不命中场景、一个边界场景。命中场景确保规则有效,不命中场景确保它不乱报,边界场景覆盖空文件、非目标文件、特殊字符串等。没有测试的规则,本质上就是一段没人维护的脚本,几周以后可能就误报得没人敢开。
规则集的组织也值得规划。单条规则文件放在rules/目录,按团队前缀命名,比如team/no-eval.js。规则越来越多之后,可以做一个入口文件统一导出。发布到团队内部 npm 私有仓库,或者直接放在 monorepo 里用路径引用,两种方式都可以,关键是版本起来之后要有明确的变更记录。
5. 把审查接入版本库主干:CI 配置与质量门禁设计
5.1 在 GitHub Actions 里跑 PR 审查
本地跑通之后,下一步是把 open-code-review 接进 CI,让每次 PR 自动触发审查。团队在 GitHub 上协作时,可以用 GitHub Actions 做一个非常轻量的 workflow。
我放在项目里的 workflow 文件大致如下:
name: open-code-review on: pull_request: types: [opened, synchronize] jobs: review: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npx @open-code-review/cli ci --base origin/main这里有一个细节坑:fetch-depth必须设为0,默认的 shallow clone 会丢掉 git 历史,工具无法对比变更范围,审查结果就会变成空跑。我见过不少团队接这类工具都栽在 checkout 深度上,看着 job 跑成功了,实际上什么都没查。
ci子命令会主动读 PR 的变更范围,把审查结果输出到标准输出,同时根据退出码决定是否阻塞合并。默认情况下,只要有error级别问题就返回非零退出码,workflow 就会失败。这个行为可以通过配置调整,保证门禁策略和团队实际节奏匹配。
如果你用的是 GitLab,思路完全一样,只是将 runner 换成 GitLab CI,命令还是同一个。核心是保证工具拿到的是完整 diff,而不是某个新建分支的快照。
5.2 质量门禁:错误级别与 baseline 机制
质量门禁设计是整个落地过程中最容易反复拉锯的部分。接太狠,团队抱怨流程太重;接太松,工具形同虚设。我实际操作下来的建议是:分级处理,不给一刀切。
打开门禁之前先明确三个级别的含义:
error:必须修复或明确豁免,通常对应 bug、安全隐患、数据丢失风险。warning:默认不阻塞合并,但应该标记出来,团队可以设定一个可接受的阈值。info:纯提示,不参与门禁,用于代码风格建议。
实际配置时,error级别阻塞 CI,warning级别允许合并但会把问题记录到报告中。比较理想的做法是分阶段引入:
第一阶段只做观察,把所有级别的问题都输出到 PR 评论区,不设置任何门禁。跑一两周,让团队看看误报率,把特别离谱的规则调好。阶段目标是建立信任感,让大家觉得工具不是在添乱。
第二阶段把error门禁打开。从观察期选出的高价值规则提升为error,其他保持warning。此时开发者提交代码时就必须正视某些问题,但不会被细枝末节卡住。
第三阶段引入warning阈值。比如允许最多 5 个 warning,超过则阻塞。这个需要团队共识,我个人不推荐太早做,否则很容易回到"形式主义"的老路上。
baseline 机制在这个阶段作用最大。.ocr-baseline.json存的是存量问题快照,审查时工具只关注新增问题,历史问题单独出报告。这样团队的改动可以逐渐消化历史债,而不是在第一天就被几千个历史问题吓退。每次合并代码时,工具会更新 baseline 文件,存量问题只会越来越少。
5.3 工具之外的团队配合
工具再强,如果流程设计跟不上,最终也会变成又一个被绕过的检查项。落地 open-code-review 的过程中,有几件工具之外的事我建议一起做。
第一件是把 PR 模板改掉。在 PR 描述里增加一个"自查清单"区域,例如:
## 自查清单 - [ ] 已在本地运行 open-code-review,无新增 error - [ ] 已补充或更新相关测试 - [ ] 涉及配置变更时已在描述中说明影响范围这个清单不是为了多一道手续,而是让作者在提交前用工具跑一遍变成约定俗成的动作。工具帮人看低级问题,清单提醒人关注更高层的事项。
第二件是重新定义评审人的职责。接入工具之后,评审人不需要再把精力花在"这里有个魔法数字"上,真正应该做的是评估设计决策、思考变更的边界、检查测试是否覆盖关键场景。我在团队里明确说了一句:如果 PR 评论里只剩下格式建议,说明工具还没配置到位。
第三件是周期性回顾审查数据。open-code-review 的 JSON 报告可以汇总到简单的数据看板里,每周看一次问题分布、规则命中趋势、误报反馈。工具的价值不止于拦住问题,还在于暴露团队代码里反复出现的模式,这些数据会直接告诉我们下一阶段该建设哪些工程能力。
6. 实测半年之后:误报、性能与维护成本问题
6.1 三个典型误报场景和根因
代码审查工具最容易被吐槽的就是误报。open-code-review 在我这边的误报率不算高,但确实存在,而且主要集中在三个场景。
第一个场景是把删除行的上下文误判成新增问题。diff 里删除和新增常常出现在同一个 hunk 中,工具如果只按行号定位,可能会把 context 里本来就有问题的老代码当成新增代码报出来。根因是变更类型判断不够精确,解决方式是在自定义规则里永远先判断ctx.kind === 'added',只在新增代码上执行高风险规则。默认配置其实已经做了这层过滤,但自己写的规则容易忘。
第二个场景是规则作用域没限定文件类型。有人写了一条规则,想检查所有代码里不允许出现某个 API,结果忘记限定语言,于是这条规则开始对 JSON、YAML、Markdown 文件也做字面匹配,误报率直线上升。这个问题的根因是match阶段写得不够严谨。我自己的经验是:每条规则的match里第一件事一定是限定扩展名。
第三个场景是模板文件和生成文件。许多项目的模板文件里会有类似${PORT}的占位符,生成文件则可能是代码打包后的产物。如果ignore_paths没有覆盖这些路径,工具就会把占位符当成魔法数字、把编译产物里的代码当成手写代码来审查。要处理这类问题,我在项目根目录的.open-code-review.yaml里把dist/、build/、coverage/、*.template.*全部排除了。这个配置看似简单,但能挡住大量垃圾报告。
6.2 大仓库性能优化实测
我们团队有一个历史悠久的 monorepo,几千个包,单次全量 diff 能达到上千个文件。最开始接入 open-code-review 时,一次 CI 审查要跑 80 多秒,整个团队都觉得太慢。后来我专门花了一段时间做性能调优,整体用时降到了 12 秒左右。
过程中最有效的几个手段:
第一,配置好ignore_paths。很多慢不是因为审查逻辑复杂,而是因为把大量无需审查的文件喂给了解析器。排除掉node_modules、dist、coverage、*.lock这些路径后,速度直接提升了一个量级。
第二,利用--diff-filter只审查需要的变更类型。默认情况下所有文件类型都会处理,但你可以按文件类型过滤,比如设置只审查.ts、.tsx、.js文件,其他类型交给别的工具处理。
第三,为规则设置执行成本标签。open-code-review 的规则可以声明cost: low | medium | high,引擎在并发调度时会优先跑低成本规则,高成本规则只在小范围 diff 时执行。这个机制很实用,比如全仓库 AST 遍历规则成本极高,如果变更只有两个文件,就没必要跑全量。
我自己还把命令行输出格式从默认表格改成--format json,因为在 CI 服务器上表格渲染本身也占时间。当然这只是锦上添花,最核心的还是路径过滤和规则分级。
6.3 让团队真正"听"工具的话:反馈闭环
最后聊一个比性能更重要的主题:规则库的健康度维护。open-code-review 这类工具最大的风险不是技术问题,而是规则越加越多、越加越乱,最终误报率失控,团队选择关闭整个工具。
我现在的做法是,把规则分三批管理。
第一批是团队红线规则,数量非常少,通常不超过十条,全部设为error,不处理就不允许合并。这些规则对应的是安全事故、资金损失、数据破坏等严重问题,没有商量余地。
第二批是通用规范规则,对应代码风格和常见坏味道,以warning为主。这些规则会定期审视,每次审视时重点关注一个指标:命中率和未被质疑的比例。如果一条规则在两个月内命中了 50 次但没有一次争议,说明它已经内化成了团队共识;如果一条规则反复被人标注为误报,我就直接关掉它。
第三批是项目特色规则,不同业务线自己维护。比如支付业务会检查金额计算不能使用浮点数,前端团队会检查不能往生产环境注入调试代码。这部分规则可以充分体现 open-code-review 的"开放"特性。
规则维护频率不用太高,一个季度一次就够。每次做规则健康度回顾时,我会把 JSON 报告和线上事故记录放在一起看,反思一个问题:上周线上出的问题,有没有可能通过一条新规则在 Code Review 阶段就拦住?如果能,就把这条规则补进去。
接入 open-code-review 半年多,我最明显的体感不是"少了很多事故",而是评审讨论的质量变了。人不再花时间去指出那些机器能发现的问题,而是把省下来的注意力放在架构、边界和测试设计上。工具本身不产生质量,质量来自团队对规则的共同维护。当一条新规则被大家接受,它就不再是我个人的要求,而是团队对代码质量的一份共同契约。