1. 为什么我最终放弃了一堆花哨工具,回归“open”的代码审查
这两年团队规模从不到十个人涨到三十多人,代码库越来越乱,我的大部分时间都耗在“帮别人擦屁股”上。不是功能开发难,而是合并请求的质量参差不齐:有人直接往主干推代码,有人过了三天才来 review,还有人 review 时全程只回了一句“LGTM”。最要命的是,很多问题到了测试环境才暴露,一查提交记录,早在 review 阶段就该拦下来。
后来我们陆续试过几款商业代码审查平台,功能确实多,但问题也明显:价格不便宜,配置复杂,而且审查数据全部存在别人服务器上。对于很多对代码安全比较敏感的项目,总感觉心里不踏实。于是我开始认真考虑另外一条路,做一套足够“开放”的代码审查方案:不依赖某个大而全的商业产品,而是基于 Git 工作流,搭配开源免费的静态检查、自动化和我们自己定制的规则,把 review 变成开发流程里一个自然、透明、强制性的环节。
这就是我做“open-code-review”这个项目的最初动机。所谓的“open”,不只是说工具要开源免费,更重要的是整个审查流程对团队每个人开放透明:谁提的合并请求、改了什么文件、CI 上跑了哪些检查、哪条评论被解决、谁最终点的合入,每一步都有据可查。代码审查这件事的价值,很大程度取决于流程的可追溯性和一致性。靠人肉盯、靠口头提醒肯定走不远,必须让规则自动运转起来。
这篇文章我不会去讲某个特定商业 SaaS 的使用教程,而是把这一整套基于 Git 体系的正向代码审查实践,和我实际搭过的方案完整铺开:包含流程设计、工具选型、自动化检查接入、门禁设置,还有我踩过的一些坑。如果你也打算把代码审查从“走形式”变成“真正的质量闸门”,这篇文章应该能帮上忙。
2. 代码审查的底层逻辑:到底在审什么,以及怎么让团队不抵触
2.1 审查的本质不是“找茬”,而是降低缺陷传播成本
先说一个我在团队里反复强调的观点:代码审查做得好不好,不在于能不能揪住同事的小辫子,而在于能不能在缺陷传播之前把它拦下来。一段代码从提交到上线,缺陷越晚被发现,修复成本就越高,这个规律在实际项目里体现得特别明显。本地编译时发现的命名问题,可能一分钟就改完了;到了合并请求里被人指出来,可能要重新跑一遍测试;等上了测试环境才发现逻辑有漏洞,牵扯到的就是功能联调和回归;真要到了生产环境出事故,那就不只是代码的问题了,还有运维成本和团队信任成本。
所以代码审查的核心目标,是在“缺陷刚刚被制造出来、还没有被更多人依赖”的时间窗口内,用尽可能低的沟通成本把它解决掉。理解了这一点,你就会明白为什么 review 的速度和密度,比 review 的形式更重要。一个合并请求放了两三天没人看,开发人员都开始做下一个功能了,再回头改这个分支的成本就会几何级上升。
我见过很多团队把 review 当成“产品上线前的安全检查”,排期排到最后一天,全员一起 review。这样做的结果往往是:时间紧、任务重,大家根本没心思细看,草草点个通过就算完事。真正有效的代码审查,必须嵌入到日常开发节奏里,每次合并请求都是一次小范围的 review,而不是攒到最后搞“大阅兵”。
2.2 用数据说话:审查覆盖率、平均响应时间、评审密度
好的代码审查流程需要可量化的数据来评估效果,不然永远停留在“我觉得我们 review 做得好”这种主观感受上。我给自己团队定过三个核心指标,简单粗暴但非常有用:
第一个是审查覆盖率。统计所有合并请求里,有多少是至少被一个人 review 过的。理想情况应该是 100%,达不到这个数字就说明流程里有旁路,有人在绕过规则提交代码。
第二个是平均首评响应时间。从合并请求创建到第一名评审人给出第一条评论之间的平均时长。这个数字反映了团队对 review 的重视程度和响应速度。我们早期的数据是十几个小时,后来通过企业微信机器人提醒和定期站会点名,逐步压到了两小时以内。
第三个是评审密度,也就是每个合并请求平均有多少条有效评论。密度太低说明大家没认真看,密度太高也不一定好,可能说明代码质量和设计统一性有问题,或者主程序员写代码太随性。我们内部的经验值是一个 200 到 300 行的合并请求,有效评论在 3 到 8 条之间比较合理。
这些数据从哪里来?如果一个团队用 GitLab 或 Gitea 这类开源平台,可以直接通过他们的 API 拉取合并请求数据和评论记录。我自己是写了一个小脚本,每天凌晨跑一次,把前一天的数据汇总成日报推送到团队的即时通讯群。这一步不用搞得太复杂,但一定要让全团队看到一个公开的数据榜,让 review 这项工作变得可见、可比。
2.3 “开放”的团队规则:评审人不设限,质量责任不只落在一个人身上
很多团队对代码审查有一个误区,觉得必须指定一个“架构师”或者“技术负责人”来当唯一的评审人,其他人都是摆设。我在实际运作中发现,这种模式有几个问题:一是负责人容易成为瓶颈,所有合并请求都在等他看;二是其他成员会产生依赖心理,觉得反正最后有人把关,自己就不用心写了。
所以我定的规则是:每个合并请求至少需要两个 approve,且这两个人不能是作者自己。这两个人是谁并不固定,可以是同组同事、下游模块的维护者、甚至是刚入职的新人。新人虽然对业务理解不够深,但他们往往能发现文档缺失、代码可读性差、注释不到位等问题。代码审查不仅是质量保障手段,也是一种极好的知识传递方式,让新人通过 review 快速理解代码库的约定和架构脉络。
规则上还有一个隐性要求,就是审查人对代码质量负有和作者同等的责任,如果一段明显有问题的代码通过了 review 并上线出了事故,审查人也要在复盘时承担责任。这个规则一开始会让很多人紧张,担心自己水平不够不敢 approve。实际运行下来却发现,正是这种共同责任的压力,促使大家在 review 时更认真,也更愿意在平时多交流、多看代码,整体团队的技术氛围反而变好了。
3. 方案选型:我为什么选了 Gitea + 自定义门禁,而不是更重的平台
3.1 自托管选型的几个硬约束:成本、数据、扩展性
先交代一下我评估代码审查平台时的几个硬指标,你也可以按这个思路来筛选适合自己团队的方案。第一个硬指标是数据主权。我们所在项目涉及不少用户的非公开数据和内部业务逻辑,代码托管在第三方商业平台总让我有点顾虑。商业平台虽然安全性不差,但真出了什么纠纷和合规要求,数据拿不拿得回来、拿回来的成本有多高,都是不确定的。所以自托管几乎是必然选择。
第二个硬指标是总持有成本。所谓持有成本不只要看软件授权费,还要算上维护工时、服务器成本、升级成本。我们团队没有专职的 DevOps,所有基础设施都是开发兼着管。所以工具必须足够轻量,部署不能太复杂,升级不能太频繁,出了问题社区里还得能找到答案。
第三个硬指标是开放扩展性。我希望所有的审查规则、检查项、自动化脚本都是代码化的,存在我们的仓库里,谁都能看到、谁都能改进。商业平台经常把核心流程封装得很死,你想改个状态流转或者加一个自定义规则,得提交工单等客服排期。对于缺乏耐心的程序员来说,这种体验是致命的。
综合对比下来,GitLab 社区版和 Gitea 是最符合要求的两个候选。GitLab 社区版功能确实强,CI/CD 一体,Merge Request 的体验也很好,但服务器资源占用偏高,我们那台 4 核 8G 的机器跑起来有点吃力。Gitea 则是出了名的轻量,一个二进制文件就能跑起来,部署极快,界面简洁,API 也很完整,虽然功能比 GitLab 少一些,但对于我们这种中小团队完全够用。
我最终选了Gitea + 自建 CI + Webhook 机器人的组合,整套方案完全开源、完全自控,改造起来也没有心理负担。
3.2 审查流程的“主干道”:模拟一个完整的合并请求生命周期
为了让没有接触过 Git 协作的人也能明白整个流程,我先用一个典型的合并请求(Merge Request)来走一遍生命周期。假设前端同事小王完成了一个“导出报表”的功能,他想把代码合并到主干分支。
第一步,小王从主干拉一个分支,命名格式是“feature/导出报表”,在这个分支上做开发提交。第二步,功能完成以后,小王在 Gitea 上发起一个合并请求,把 feature 分支合入 main 分支,同时填写描述,说明改了哪些东西、测试了哪些场景、是否有需要评审人特别注意的地方。第三步,他点击创建的同时,Gitea 的 Webhook 会自动触发 CI 流水线,在独立的容器环境里执行代码编译、单元测试、静态检查等任务。第四步,CI 跑完以后,状态会回写到合并请求上,如果检查不通过,这个合并请求会被一个分支保护规则挡住,谁都合不进去。第五步,代码评审人在 Gitea 页面看到这个合并请求,点开“文件变更”逐行查看,留下评论和小王讨论。第六步,小王根据评论修改代码,产生新的提交并推送到分支,CI 自动重新运行。第七步,所有人都满意后,至少有两个人点了 approve,合并按钮才会亮起来,小王可以自己点“合并”把代码合进主干。
整个流程看起来好像没什么特别的,但这套流程里每一个环节都是可以被自动化工具和门槛规则强化或卡控的。之所以强调这一点,是因为代码审查最大的敌人其实是“例外”。如果没有工具层面的硬性约束,靠自觉和口头要求,总会出现紧急情况下直接往主干推代码的情况。一次可以理解,两次三次,规则就形同虚设了。所以必须在流程的“主干道”上放上自动护栏,让绕过规则比遵守规则更麻烦。
3.3 分支保护与权限设计:谁来把关,谁能合并
分支保护是这套方案里最关键的一个技术点。在 Gitea 的仓库设置里,找到“Branch Protection”,然后对主干分支做三件事:
第一,启用“禁止直接推送”,也就是任何人不允许直接往 main 分支 push 代码,所有变更都只能通过合并请求进入。这一步就把“绕过审查”从技术上堵死了。
第二,启用“要求审查通过”,设置最少 1 或 2 个审查人 approve 才允许合并。新项目或非核心仓库可以设成 1,核心业务仓库建议设成 2,具体看团队规模和历史事故发生频率。
第三,启用“要求检查通过”,和 CI 联动起来。CI 任务没有跑完或者跑失败了,合并按钮保持灰色,谁也没办法强行合入。
权限设计上,我坚持了一个“最小权限”原则:默认所有成员只能创建分支和发起合并请求,main 分支的保护由仓库管理员统一维护。特殊情况下,比如线上出了紧急事故,需要临时绕过审查,一定走例外申请流程,在群里说明理由,事后补 review。例外通道一定要留出来,不然团队会用脚投票,直接搞出一堆假分支和手动部署操作来绕开你的流程。给一个合理范围内的“逃生通道”,反而更能保证日常规则的严肃性。
4. 让合入门禁有牙齿:CI 集成与静态检查配置实战
4.1 从“建议”到“拒绝”:门禁检查的优先级分层
很多团队在搭建代码审查流程时容易陷入一个误区:想一次性上很多检查工具,把 CI 跑成一个“全家桶”。结果就是构建时间越来越长,误报越来越多,开发者开始对流水线里的警告麻木,最后连真正有用的错误也被淹没在噪音里。
我的做法是对所有检查项做一个分级。P0 级别是阻断项,出现任何一个就必须 fail 构建,不允许合并,比如编译错误、单测不通过、安全漏洞扫描出高危问题。P1 级别是强提示项,不会直接挡住合并请求,但在审查界面会醒目标出,评审人需要针对这些提示做出明确判断,比如未使用的变量、明显的代码坏味道、覆盖率显著下降。P2 级别是建议项,只是记录到评论里供参考,比如格式化风格微调、命名建议、日志标准化建议。
实际执行下来,P2 级别通常由机器人根据规则自动生成评论,作者觉得有道理就顺手改掉,觉得没必要就回复一句“ignore”,不会形成无休止的拉锯。分级策略的核心理念是:不要试图用工具替代人的判断,而是让工具替人过滤掉 80% 显而易见的低级问题,把评审人的时间和注意力释放出来,集中到真正需要人脑判断的设计和逻辑问题上。
4.2 选什么检查工具:不同语言栈的实用清单
这块内容比较长,我先按开发语言给出我们实际用下来比较顺手的工具清单,都是开源的,可以直接集成。
对于 Go 项目,首选 golangci-lint,它是很多 linter 的聚合器,里面包含 staticcheck、errcheck、govet 等常用工具,只需要一个配置文件就能搞定,而且支持并发,速度比较快。配置上我一般开启go vet、staticcheck、errcheck、ineffassign、unused这几组规则,太偏门或者误报率高的规则宁可关掉。
对于 TypeScript / JavaScript 项目,ESLint 可以说是标配,配合 typescript-eslint 插件使用。除了常规规则,我还会开启no-unused-vars、@typescript-eslint/no-floating-promises这类容易踩坑的规则。样式检查用 Prettier,主要是统一代码格式,避免 review 时因为格式问题争论不休。
Python 项目我用的组合是 ruff + mypy。ruff 是一个用 Rust 写的 Python linter,速度极快,替代了传统的 flake8 和 isort。mypy 负责类型检查,Python 项目一旦规模上去,静态类型的重要性会被放大很多倍,很多重构隐患都是 mypy 先发现的。
通用型检查还有一个很有意思的工具叫 commitlint,它检查提交信息的格式是否符合 Conventional Commits 规范。你可能会觉得提交信息格式有什么好管的?真实情况是,当你在几十个提交里索引某一个功能变更时,杂乱无章的提交信息会让你疯掉。规范提交信息不光是仪式感,它直接决定了自动生成 CHANGELOG 的可行性。
4.3 把检查结果“翻译”成人话:Gitea + Forgejo 的机器人评论实践
工具选好了只是第一步,真正影响体验的是检查结果怎么展示给开发者。如果 CI 只是在详情页里贴一个日志文件的链接,让开发者自己去翻那几百行输出,体验是非常糟糕的。我在这里花了不少功夫,做法是写了一个小机器人服务,接收 CI 完成后的 Webhook 回调,解析各个检查工具的 JSON 输出,然后把这些结果转换成 Gitea 合并请求下的结构化评论。
具体形式是:机器人会发一条总览评论,分三个区块,分别是“阻断问题 (Blocking)”、“提示问题 (Warnings)”、“代码格式建议 (Style)”。针对每一个具体问题,评论里包含文件名、行号、问题描述、对应的官方文档链接和修建议。比如 ESLint 报了一个“no-floating-promises”错误,机器人会直接指出在哪一行,还附带一个修正示例。开发者不需要跳出当前页面去查文档,直接在内联评论区就能完成修改。
这样做的收益是很明显的。以前开发者看到报错会心烦意乱,现在看到的是清晰的待办清单,照着处理就行。静态检查的“存在感”大幅降低,不再是一个需要单独打开去看的页面,而是合并请求流程的一部分,跟队友的评论处于同等的位置。尤其对于新入职的同事,这种“机器人老师傅”式引导能让他们快速上手团队规范,省去了很多反复私聊解释的时间。
4.4 避免“CI 过重”的经验:缓存策略与增量检查
随着仓库变大、检查工具变多,CI 时间会越来越长。我们早期最疯狂的时候,一次全量检查要跑十五分钟,开发者提交一次代码要等很久才能看到结果,体验非常糟糕,大家开始厌烦这个流程。
这里的核心解法是缓存和增量检查。缓存方面,Gitea 的 Actions Runner 支持缓存工作区依赖,Go 的模块缓存、npm 的~/.npm缓存、Python 的 pip 缓存都可以配置,这一块通常能把依赖安装时间从几分钟压缩到几十秒。
增量检查方面,不是每次合并请求都跑全量 lint 和全量单测,而是先通过git diff拿到本次变更涉及的代码文件,只对这些文件和与它们有直接依赖关系的模块做检查。当然,全量编译和必要的单元测试还是需要跑的,但 lint 和复杂度检查这类高耗时低变化率的任务完全可以做增量。我写了一个小脚本来计算变更文件集合,再传给各个工具做路径过滤。实施以后,CI 总时长从十五分钟降到了四分钟以内,团队幸福感直线上升。
5. 从流程到机制:代码评审时那些“常规文档里不会写”的经验教训
5.1 评论的语气和措辞,直接关系到 review 文化的走向
这条心得听起来不像技术问题,但我觉得它是决定代码审查制度能不能持续运行的关键因素。客观地说,程序员这个群体自尊心普遍比较强,对负面反馈很容易产生防御心理。如果 review 的评论全是“这里写得有问题”“这个实现太烂了”,用不了几次,团队里就会出现私下抱怨,进而演变成互相敷衍、走形式。
我们内部制定了一个“评论三原则”。第一,指出问题的同时,说清楚影响。不是说“这样写不行”,而是说“这样写在并发场景下会丢数据,建议改成 X 方式”。第二,遇到不理解的地方先提问。可以说“这里为什么不用现成的 util 方法呢?是有什么特殊考虑吗?”,而不是“你为什么要搞这么复杂”。第三,对好的实现及时点赞。一行让人眼前一亮的代码、一个优雅的边界处理,都值得一句明确的肯定。正反馈在协作里非常稀缺,哪怕只是简单说一句“这个用 guard 提前返回的做法很清晰”,对方也会更愿意认真对待你的其他建议。
听起来有点软性,但请你务必尝试一下。真实情况是,在代码审查里,情绪安全比逻辑正确更重要。一旦团队里的人对 review 产生了恐惧或者厌烦,再完美的流程设计也会在执行层面失效。
5.2 评审密度怎么控制,以及“谁看什么”的分层策略
我们团队的规模决定了不可能让所有评审人都看所有代码,那样会把人累垮。实践中我按模块和关注点把评审人分了层级,有点像“编辑部审稿”的逻辑。
基础层是自动检查,跑 CI、跑 lint、跑安全扫描,所有合并请求都覆盖。主要层是两名指定评审人,负责整体把握这次变更是否符合架构方向、有没有明显逻辑漏洞、测试覆盖是否合理。关注层是按需邀请的专家,比如这次改动涉及数据库表格结构,那就拉上负责数据库性能的同事看一眼索引设计和查询写法;涉及前端组件库和 UI 规范,就拉上设计系统的维护者确认一下。
划分层级的价值在于,让合适的人去关注合适的问题,而不是逼每个人对每个合并请求给出一模一样的 review。强制全员都看,实际效果往往是谁都没认真看,不如让少数人认真看完,其他人只做 smoke test。
5.3 合并请求大小拆分的实操建议:为什么大 PR 是质量黑洞
我在这个项目里一个非常深刻的体会是:合并请求的体积,决定了 review 的生死。一个改动超过 1000 行的合并请求,评审人的注意力曲线会快速下滑,到后期基本就是在乱点了。研究早就表明,代码审查的缺陷发现率会随着单次审查代码行数的增加而显著下降,这一点在实践中体感非常强烈。
所以我们执行了一个不成文但很硬性的规则:核心业务逻辑的单次合并请求尽量不要超过 400 到 500 行,超过就要考虑拆分提交策略。拆分方式有三种常见思路:按功能阶段拆、按模块边界拆、按重构与功能拆。
按功能阶段拆,适用于横跨多层的完整需求。把一个端到端的特性拆成“后端接口实现”、“数据库迁移脚本”、“前端页面交互”三个合并请求,每个请求的上下文都更精简。按模块边界拆,适用于改动涉及多个相对独立子模块的情况,把每个子模块的改动单独成一个合并请求,降低耦合。按重构与功能拆,是最推荐的一种方式。重构代码和新增功能的行为搅在一起,是评审人最痛苦的事情,因为你无法判断这次变更是纯等价改写,还是隐含了行为变化。
给一个最简单的判断标准:当你看到合并请求描述里同时出现了“重构了 XX 模块”和“新增了 YY 功能”,这通常就是一个该拆的信号。
5.4 紧急修复的绿色通道:怎么处理“火烧眉毛还得改代码”
任何流程都要考虑例外情况,否则就活不下去。线上服务挂了,用户报障铺天盖地,这时候不可能要求开发者先开分支、跑完完整 CI、等两个人 approv e再合并。我们制定的紧急通道是这样的:紧急情况可以在群里说明原因,然后直接推送一个 hotfix 分支到主干,但推送后 24 小时内必须补提一个合并请求,把这次 hotfix 里的改动追认为一次正规的代码变更,并且要在描述中说明“紧急合并原因”和“事后 review 结果”。
这个机制的关键在于,允许你打破流程,但不允许你偷偷打破流程。每一笔紧急合并都会被记录在案,定期站会上过一遍,分析哪些是真正无法避免的紧急情况,哪些其实是可以提前规避的。长期跑下来,紧急合并的频率会显著下降,因为团队会从每次事故里总结出规则来填补流程空白。如果你把这个通道彻底关闭,大家会想办法绕过系统,反而更危险。
6. 这套方案实际运行中的效果:数据与团队体感的变化
6.1 上线三个月后的几个关键指标对比
方案上线三个月后,我做了一次阶段性统计,对比了前后的几个核心指标,数据非常有说服力。
合并请求的平均生存周期,从原来的 3.2 天降到了 1.1 天。原因是流程规范化以后,合并请求创建得更加频繁但单个体积更小,CI 反馈速度快,评审响应也快,整个交付闭环顺畅了很多。线上缺陷数量,按每月统计,从十位数降到了个位数。并非所有缺陷都是 review 拦下来的,很多是自动检查在开发早期就发现了,但整体质量基线确实明显上移。测试环境部署失败率,从原来的四次里失败一次,降到了十次里失败一次以内。原因是 lint 和基础单测在合并前就把很多低级错误滤掉了,测试环境的精力能集中到联调上。
最让我惊喜的是知识传递的效果。新同事熟悉代码库的时间明显缩短,因为每一次 review 都是活生生的“架构课”,评审人的评论里包含了大量关于系统设计背景和编码约定的信息,这比看文档学得快得多。
6.2 团队内部真实反映:吐槽声少了,信任感强了
数据能说明一部分问题,但团队的真实感受更值得关注。推行这套流程初期,吐槽是免不了的,主要集中在两条:一条是觉得“好麻烦,写代码还要被机器人管着”,另一条是“我写的代码被人逐行看,好有压迫感”。
坚持跑了一个月以后,风向开始悄悄转变。原来强烈反对的同事开始主动在合并请求里加注释,说明自己某块代码为什么这样实现,以减少评审人的疑惑。还有同事开始主动帮别人 review,理由是“反正咱们规则要求至少两个人 approve,我看看还能学点东西”。这其实是很正向的一个信号,说明 review 从一种被动要求变成了团队内生的学习习惯。
信任感的增强也很明显。以前上线前,负责人总会精神紧张,要反复确认代码是不是没问题。现在大家相信流程已经过滤了大多数能自动化拦截的问题,而人的精力集中在更高层次的风险讨论上。这种“安全感”,才是这套开源方案带来的最大收获。
7. 常见问题与我踩过的一些坑
7.1 合并请求卡在“检查中”状态,怎么排查
Gitea 的合并请求状态是整合了 CI 结果的,偶尔会出现 CI 明明成功了,合并请求却一直显示“检查中”的情况。这个问题的根源通常是 Webhook 回调通知丢失,或者 Actions Runner 在任务结束后没有正确上报状态。
排查思路分三步。第一步,先到 Gitea 的后台查看最近有没有收到该仓库的 Webhook 投递记录,看是否出现 500 错误或者超时记录。第二步,如果 Webhook 投递正常,那就看 Actions Runner 的日志,确认任务执行完以后的状态上报是否成功。第三步,可以手动调用 API 查询该合并请求的 commit status,判断是哪一个提交的状态没有被更新。
我踩过的具体坑:早期我用自建 Runner 跑任务的时候,没有给 Runner 的环境变量里配置足够的超时时间,任务超时被杀掉后状态没有回写,导致合并请求一直显示“检查中”。后来我在 Runner 的启动命令里显式指定了--job-timeout 30m,问题就解决了。
7.2 机器人评论刷屏,如何避免“评论疲劳”
静态检查接入初期,机器人会在每个合并请求下生成几十条评论,开发者看不过来,干脆忽略。这是典型的“评论疲劳”问题,处理不好会让整个审查制度失去效力。
我的解法是对评论做聚合和频控。所有相同类型的问题合并成一条总览,每个文件下的问题集中在一个线程里讨论。修改后重新推送,机器人不会重复“轰炸”已解决的问题,只追加新发现的问题。另外给机器人评论设置了“只在检查不通过时发”的规则,如果全部通过就只发一条简短的“全部检查通过”消息,很多无意义的噪声直接消除掉了。
如果你发现机器人发了一大堆“建议把双引号改成单引号”这类格式化意见,我建议不要开这个检查。格式化交给 Prettier 或 gofmt 在保存和提交前自动处理,而不是在 review 阶段用评论的方式让人肉去改,这是低效且招人烦的。
7.3 分支保护与管理员权限的冲突
Gitea 的分支保护选项里,默认“管理员不受保护规则限制”是开启的。这个设置非常坑,某次我临时帮同事提交一次代码,以管理员身份直接推送到 main 分支,居然成功了,事后才知道因为管理员不受保护限制。如果你希望分支保护规则真正有效,一定记得把这一项关掉。
另外,如果团队里有多个管理员,大家默认拥有系统管理的最高权限,建议给管理员也建普通账号用来日常开发,系统管理账号只做仓库设置和成员管理,避免各种误操作。
7.4 团队成员跨时区协作,评审等待时间过长
我们团队有一个远程同事在海外,时差比较严重,从他发起的合并请求常常要等国内同事白天上班才能有人 review,反之亦然。等待评审时间过长会让开发流程明显变慢。
我的解决方法有两个。第一,把“等待评审”的合并请求纳入每日站会的同步范围,大家一起看一眼有多少待 review,优先处理阻塞项。第二,调整分支保护规则,非核心仓库允许作者本人 approve 自己的合并请求,前提是 CI 全部通过且至少有一名其他同事 comment 过。这个规则的出发点是:低风险仓库不需要那么严格的制衡,但为了保留透明性,还是要留痕。核心仓库依然严格执行两人 approve 制度。
8. 后续还能怎么扩展:从“人靠流程”走向“流程靠数据”
目前这套 open-code-review 方案已经稳定运行了大半年。接下来我计划做两件事:一件事是把所有机器人评论的质量纳入定期 review,收集误报率数据,持续调优规则配置;另一件事是根据 git 历史数据自动生成团队代码热力指数,哪块代码近期改得频繁、哪些模块的缺陷率高,都可以通过自动化方式暴露出来。
这一块很多人会忽略,代码审查本身也是一个可以持续优化的系统。不能搭完流程就不管了,要像对待产品一样对待它,根据运行数据随时调节参数。规则太松,缺陷漏网;规则太紧,团队反感。找到适合自己团队的“甜蜜区间”,本质上是一个不断迭代、不断校准的过程。
如果你也在酝酿给团队搭建一套开源、透明、可控的代码审查体系,不要急着复制我的整套配置。可以先从最小闭环跑起来:Git 分支保护加一个 CI 门禁加一条 review 通过要求。先把框架搭好,再逐步填充你团队真正需要的检查规则和自动化流程。工具永远服务于人和效率,这一点始终不要忘了。