从LGTM到真审查:open-code-review落地实践与避坑指南

📅 发布时间:2026/10/21 18:36:15
从LGTM到真审查:open-code-review落地实践与避坑指南
代码审查这事儿做容易做好真不容易。大多数团队是从某一天上线出事故开始才意识到必须做 code review结果 review 制度定了合并前需要有人点“批准”大家就开始找最熟的人秒批。几个月后回看评论全是 LGTM事故该出还是出。后来我把 review 流程重做成一个开放方案的载体内部代号 open-code-review用配置和脚本把审查门槛显式地立起来才慢慢扭过来。这篇文章不会讲什么石破天惊的高深架构就讲我们是怎么设计、怎么落地、踩了哪些坑给同样想认真做 review 的中小团队一个参照。1. 为什么我会自己搭一个 open-code-review1.1 代码审查的“形式主义”怪圈先说一个很典型的场景团队定了规矩所有合并必须先过 review。于是大家就会想办法让这个流程尽量“不痛”。常见操作是找平时关系最好、回复最快的同事发一句“帮忙看一下”然后对方不到一分钟点 Approve评论里写个“LGTM”就完事。为什么会出现这种情况因为多数团队把 code review 当成“上线前的一道审批”而不是“变更质量的保障环节”。只要审批人最终点头代码就可以走那审批人当然会选择最省力的路径。最麻烦的是审查过程如果不留下任何结构化记录后面出了问题根本没法复盘——究竟是谁在什么时间点放过了这个设计隐患当时讨论过哪些替代方案这些问题没人能回答。我见过更极端的案例两个后端同事在 IM 里吵了一晚上最后决定“先这样后面再改”但第二天人都忘了。评论散落在聊天记录里PR 描述里干干净净好像一切都没发生过。这就是典型的“过程有记录无结论靠自觉”的形式主义怪圈。1.2 这个方案到底想解决什么问题open-code-review 的设计目标并不是“发明一套新的代码审查理论”而是把团队已经约定的规则变成一套显式、可配置、可观测的流程载体。核心就三句话每个变更在合并前至少得到一次来自非作者的、带编号的人工审查自动检查负责挡掉低级问题人工负责判断设计和逻辑每一次审查意见、结论、耗时、轮次全部沉淀成结构化数据。听起来不复杂但真要把这三件事落地至少需要解决几个具体问题谁来审、什么时候开始审、自动检查没过能不能进入人工评审、状态怎么流转、超时怎么办、沉淀的数据怎么统计。这些问题如果靠人盯最多撑三个星期如果靠平台自带的 review 功能又往往只能覆盖“有人点按钮”这一个点。所以我把 open-code-review 定位成一个“流程脚手架”它不强求你用某个特定平台而是提供一套可以嵌入现有工作流的规则引擎和脚本工具。我们团队用的代码托管平台本身有 PR 和分支保护能力但缺少我们想要的“审查状态机”和“数据沉淀层”于是用 open-code-review 补上了这一层。1.3 为什么不直接用商业平台而选择开源组合很多人第一反应是直接用代码托管平台的企业版或者买商业的审查工具不就行了吗我承认开箱即用的商业方案确实很省事但对我们这种规模和技术栈偏小众的团队来说有几个绕不过去的坎。第一是定制成本。商业工具的审查规则往往以平台能力边界为上限比如“按目录分配审查人”这种看似平常的需求在不同平台上实现难度天差地别。我们希望规则文件能进 Git 仓库、能跟着分支一起走而不是在网页后台点来点去。第二是数据可控。审查记录里包含很多内部讨论细节我们希望 CI、日志、后续的 AI 分析都能直接消费这些数据而不是被锁在某个平台里靠人工导出。第三是成本。几十个人的团队为 review 单独付一笔不少的年费总觉得不如把预算留给测试环境。所以最终选择是“开源组合 少量自研脚本”。open-code-review 这个名字更像是我们对这套组合的统称底层是代码托管平台的分支保护中间是 CI 里跑的检查上层是一个轻量状态机和数据收集器。实践下来自由度比商业工具高很多维护成本也没有想象中那么吓人。2. open-code-review 的核心机制拆开看其实很简单2.1 把评审变成一条显式状态机很多 review 流程做得不好是因为状态是隐性的。PR 开了到底该谁去评现在到了哪一步是等作者改、还是等第二位审查人确认这些问题全靠大家在群里问信息极不对称。open-code-review 用状态机把流程显式化了。我们定义的状态包括draft还在开发中不需要人工介入ready作者认为可以评审了自动通知审查人reviewing有至少一位审查人正在看changes_requested审查人提出了修改意见回到作者approved满足全部审查条件具备合并资格merged变更合入主干流程结束。这条状态流的好处是所有人打开 PR 都能一眼看出“卡在哪”。更重要的是状态是可脚本化的。我们在 CI 里挂了一个监听脚本当 PR 标题从[WIP]变成[Ready]时自动打上ready-for-review标签并通过机器人把待审队列推给对应审查人。作者不用再手动找人审查人也不会错过通知。状态机本身不复杂复杂的是状态之间转换的触发条件。我们的规则是自动检查全部通过才能进入reviewing人工审批通过后如果还有修改状态会回到changes_requested并重新走一遍检查只有处于approved的 PR 才允许点合并。这样做虽然多了一些步骤但每个人都清楚“现在是绿灯还是红灯”。2.2 自动化检查做守门员人工评审做裁判很多团队把“CI 通过”和“代码审查完成”混为一谈这其实是个大坑。CI 验证的是“程序能不能跑”人工评审回答的是“这个设计合不合理”。两者的目标不同不能互相替代。open-code-review 的定位是自动化检查是守门员人工评审是裁判。守门员先过滤掉格式问题、明显 bug、单测失败裁判才能把有限精力放在代码语义和架构合理性上。我们的 CI 流水线里固定挂了这样几个动作编译或类型检查自动化单元测试静态检查与代码风格检查变更影响范围分析脚本。只有这些全部成功后机器人才会在 PR 上放行ci-ready的标签同时把审查人加入候选列表。如果 CI 挂了审查人界面会出现明确的“等待自动化检查通过”提示避免“人来代替机器把关”的情况。这个设计还有一个隐性好处审查人不会因为每次都看到一堆低级问题而产生倦怠。以前打开 PR 先看到缩进不对、命名不规范心情已经坏了一半现在人类面对的内容是经过机器过滤后的纯逻辑部分耐性自然好很多。2.3 为什么元数据设计决定了流程能不能持续这是我整个项目里最想强调的一点review 流程能不能持续往往不取决于规则多严格而取决于有没有把过程数据留下来。open-code-review 在每次事件产生时都会往指定目录写一组结构化的 JSON例如{ pr_id: open-code-review-demo-42, actor: reviewer_a, action: approve, round: 2, timestamp: 2024-06-14T10:22:31Z, duration_minutes: 45, conclusion: approved, labels: [backend, refactor] }一行行看这些数据微不足道但累积一个月就能回答很多关键问题哪个模块的审查等待时间最长哪位审查人的意见动不动就导致重写哪些类型的 PR 平均轮次最高没有元数据这些问题只能靠直觉猜。更重要的是这些 JSON 会成为后续自动化的基础。比如我们要做定时统计、生成周报、甚至训练一个“常见问题预审模型”都只需要对着这些数据做二次处理不需要再去爬网页。这也是“open”这个词在我们项目里的真正含义流程数据对所有授权工具开放而不是锁在某个 UI 里。3. 从零落地open-code-review 的真实搭建过程3.1 配置仓库、分支保护和审查模板先说明一下我下面写的是我们团队在真实落地时采用的方案不代表你必须照搬。结构上项目根目录下新增了一个.ocr/目录用来放 open-code-review 的规则文件和脚本。第一步是分支保护。以我们用的平台为例核心配置是主分支禁止直接推送所有变更必须通过 pull request 合入至少需要一位非作者的批准所有状态检查必须通过。这些规则现在属于平台基础能力但过去我们根本没有认真配置过默认是“什么都能直接推”。第二步是写一个团队自己的审查模板。模板很重要但不能写成繁琐的填空题否则作者会敷衍。我们的 PR 模板只要求三个区块这个变更解决什么问题测试验证结果如何风险点/需要审查人重点看的地方。模板不是给平台看的是给审查人省时间用的。模板填得清楚审查人一上来就有上下文不需要自己翻代码猜意图。3.2 接入 CI 机器人让上下文留在变更单里流程要自动化靠的是机器人脚本。我们写了一个轻量监听服务订阅 PR 的打开、同步、评论、状态更新事件。它做几件事把 CI 结果回写到 PR 评论根据标签触发审查人提醒维护一个待审队列的看板索引。先看回写逻辑。原来 CI 的通过状态在平台页面上已经很显眼了但人总是不喜欢切页面。我们让机器人以评论的方式总结单测通过率、静态检查是否有 warning、有没有超时。评论模板放在.ocr/templates/ci-summary.md里脚本只是往里面填数字。再有一个小细节机器人触发要带幂等键。比如同一个 PR 在同一个 commit 上跑了两次 CI评论不要重复发。我们在事件体里取commit_sha check_name作为唯一键如果已经回写过对应结果就跳过。这个坑看起来小踩过一次才知道刷屏评论有多烦人。3.3 设置审查人指派规则不靠运气人工指派审查人是 review 流程里最容易得罪人的环节。早期我们靠作者自己 人结果固定被 的只有少数几个老员工新人很少有机会审别人也就很难通过这种途径成长。open-code-review 里写了一个简单的指派规则表routes: - path_prefix: [backend/] reviewers: [team-backend] - path_prefix: [frontend/] reviewers: [team-frontend] - path_prefix: [infra/] reviewers: [team-platform] policy: max_open_reviews: 5 max_per_day: 8 skip_weekend: true round_robin_weight: 1.5运行原理也不复杂根据变更路径粗筛出候选团队候选团队内部按“最近 7 天累计审查数量”做轮询数量少的优先同时把每个审查人的待审上限纳入约束比如手里已经有 5 个 open review就不再派新的。这个规则不是第一天就全量上线的。第一版我们只用了最简单的前缀匹配和轮询跑了两个星期才加入“max_open_reviews”和“max_per_day”这两个限制。原因后面会详说反正没有它规则就是另一场灾难。3.4 日常使用中的命令和状态流转open-code-review 提供了一个很薄的命令行入口日常操作主要就几条命令ocr ready把 PR 状态从 draft 切到 ready相当于通知“可以开始审了”ocr list --mine查看自己名下的待审 PRocr request --reviewerxxx在规则基础上手动补充一位审查人ocr retryCI 检查全部重跑一遍指定 commit 后生效。每条命令最终都会调用平台的 API但我们的原则是“不引入额外认证负担”。所有命令都复用开发者本地已经配好的平台 token不重新维护一套账号体系。比如ocr list --mine的输出是一张简单的表格PR 号、模块、作者、等待时长。对于审查人来说每天上午和下午各跑一次就能代替“在群里被 ”的被动模式。对于管理者还可以用ocr stats --since 30d拉出最近一个月的数据用来开周会。4. 落地两个月我踩过的几个坑4.1 指派规则误伤主力差点把人累垮第一版指派规则上线的第二天我一度以为系统出 bug 了一位后端同事的个人待审列表里突然多了 9 个 PR其中 5 个是同一个下午加进来的。查了日志才发现轮询算法没有考虑“阈值上限”只要候选池里人数不够就会反复循环到状态最空闲的那个人身上。那老哥正好在做一个平台迁移一天里开了几次短会每次回工位都能看到 bot 又提醒他有新 PR 待审。第三天他直接在群里发了一句如果再这样下去要么调走我要么把 open-code-review 删了。后来我们把max_open_reviews: 5和max_per_day: 8加进规则并且对“休假状态”做了一个简单处理只要审查人近三天没有主动合入过任何 PR系统就默认跳过。这个处理不优雅但非常有效。从那之后我再也不相信“纯轮询就是公平”这种说法。4.2 CI 并发检查与合并窗口的冲突第二个坑是自动化太勤快导致的反效果。团队习惯是上午集中提交下午集中评审所以 CI 高峰常常卡在午后。问题来了平台的状态检查对同一个 PR 是串行更新的一批 PR 同时跑同一套 CI 时经常出现“某个检查被 queued 了 20 分钟”的情况审查人明明想批却被状态机卡在reviewing之前只能干等。我们当时的处理方式比较粗暴给 CI 并发队列加了一个上限把优先级更高的“等待评审中的 PR”插队提前跑。实现方式是给 PR 加一个标签needs-reviewCI 脚本读到这个标签时优先级默认提升一级。这个做法在多数情况下有效但它本质上只是把拥堵往后挪了。真正解决问题的是后来的“合并窗口”制度每天两个固定合并时段比如 11:30 和 16:30合并前统一清理积压的绿灯 PR。这样 CI 的并发压力分散审查人也有明确的处理节奏不用在群里隔几分钟问一次“能不能合了”。4.3 海外同事跟进不及时怎么办我们团队有几名海外同事时差基本覆盖了凌晨时段。最初 open-code-review 的自动提醒用的还是“实时通知”模式结果海外同事常常在自己的休息时间收到一堆 bot 消息体验很差。我们采用了一个分层的跟进策略。PR 进入ready状态后前 4 小时只在看板组件上高亮不主动发通知4 小时后机器人对审查人发一次站内信8 小时后如果还是没有状态变化系统会把 PR 升级为“待协助”由主持人手动决定是否添加额外审查人而不是无脑 全体。这个缓冲机制还带来了一个意外的正效果很多作者在提交前更容易自己先检查一遍因为知道一旦标记 ready至少几小时内不会有任何打扰。反向思考一下过度追求响应速度有时候反而破坏了深度思考的时间。5. 用数据看看 open-code-review 到底改变了什么5.1 客观指标评审率、平均轮次、耗时落地两个完整迭代周期后我做了一次数据对比。我们不追求“评审率 100%”这种表面光鲜的数据而是看这几项指标落地前一个月落地后一个月合并前经过人工评审的比例约 18%97%平均每个 PR 审查轮次0.21.4合并后一周内出现线上问题比例6.1%2.3%平均从 ready 到 merge 的时间7.2 小时11.5 小时平均耗时变长了但线上问题率降了这个交换我觉得值。更值得注意的是“人工评审比例”从 18% 跳到 97%——剩下 3% 是一些热修复我们走了特殊的白名单通道属于主动例外不是系统漏洞。很多人可能会担心“评审时间变长会不会拖慢迭代”。我的回答是真正拖慢迭代的不是审查本身而是发现问题的时间点。过去问题往往在发布后才暴露修复成本翻好几倍现在只是把成本提前支付了而且支付得明明白白。用财务的说法这相当于把或有负债变成当期费用账面上更好看实际上也更健康。5.2 团队感受从“被挑刺”到“有人兜底”数据之外我更在意的是大家怎么看待 review。上线后第二周我私下问了几位同事发现一个共性最初大家把它当成“被检查、被挑刺”后来慢慢变成“有人帮我看一眼心里更稳”。这种心态转变一方面靠流程顺畅另一方面也得益于模板里的“风险点/需要重点看的地方”。作者主动把最没底的地方圈出来审查人就不再是从头到尾找毛病而是回答具体问题。比如有同事写“这里用了乐观锁但没完全确定并发场景是否覆盖请帮忙看看”审查人一看就知道重点在哪。这比“全局 review”的效率高得多。当然也不是没有吐槽。有人觉得“自动指派审查人”让自己没法挑简单的任务也有人抱怨“非作者批准”这条规则让依赖关系复杂的 PR 等更久。但整体上团队开始默认“所有代码在合并前被另一个人看过”这个默认值一旦被接受后面所有优化都是在这个基础上做加法。6. 接下来我还想做的扩展6.1 跟内部发布系统打通目前 open-code-review 的终点是“merge”但 review 的价值不应该在合并后立刻消失。我们内部有一个自建发布系统支持灰度、回滚和发布单追踪。接下来的想法很简单当 PR 触发merged事件时自动生成一条初始发布记录把审查人、审查轮次、最终结论和合并的 commit 关联过去。这样做的直接收益是后面如果某个版本出了线上问题负责排查的人可以直接从发布单跳到对应的 PR看到当时的 review 记录——谁审查的、提过什么风险、有没有被采纳。这比现在“出问题之后去 Git 历史里翻 commit、再猜是哪个 PR”要高效得多。实现上不需要给 open-code-review 塞太多新功能只需要在事件回调里多接一个 webhook。真正的难点是让两边的事件结构对齐避免 PR ID 和发布单 ID 互相找不到对方。我会先把映射关系写到一个本地表里跑两周看覆盖率再决定要不要做成实时同步。6.2 把历史审查数据喂给 AI做预审另一个方向是我个人很感兴趣的用历史的审查评论做一个小型 AI 预审器。open-code-review 沉淀下来的 JSON 有一个很大的价值——它们把“审查意见”和“最终是否被接受”关联在一起。我们可以把这些数据改造成训练样本被多次拒绝的模式多半是高危模式被一次通过的描述则可以作为正面例子。我的初步打算是先不对代码内容做强理解而是对 PR 描述和评论做语义摘要。比如自动提取出“风险集中在并发控制”“改动影响数据库表结构”“涉及第三方 API 兼容性”这样的标签提前标在 PR 列表里让审查人一进来就看到提示。这不替代人但能大幅减少信息检索的时间。这个方向短期内不会直接做进 open-code-review 主线里因为需要更光鲜的数据清洗和模型选型。但已经养成的好习惯是每天产生的审查数据我们一条也不会丢。反正都是落盘的 JSON未来想怎么消费都行。我个人的体会是open-code-review 真正改变的不是工具链而是团队对“合并代码是私事”这个默认假设。规则再轻也要先立起来记录再碎也要先存下来。如果你也想在团队里做类似的事情我的建议是别一次性追求完美第一周只做两件事让所有变更必须走 PR让每个 PR 必须有一位非作者批准。先跑起来再慢慢调指派、加统计、上机器人。工具是死的流程是活的关键是让团队每个人都觉得“这不是为了卡我是为了我们一起少踩几个坑”。