28、代码审查:Review 流程设计,Checklist 制定,自动化审查工具(Reviewable, Gerrit)

代码审查这件事,我做了十几年。说实话,刚入行那会儿我觉得这玩意儿就是浪费时间——自己写的代码自己最清楚,干嘛还要别人看?直到有一次,我提交了一个看似完美的内存池实现,结果被同事揪出一个隐蔽的 double-free 漏洞。嗯,从那以后,我再也不敢小看代码审查了。

今天咱们聊聊 C++ 大型项目里的代码审查。这不是简单的「你帮我看看代码」,而是一整套工程实践。我会从流程设计、Checklist 制定,再到工具选型,把这块讲透。

为什么大型项目必须搞代码审查?

你想想看,一个 C++ 项目动辄几十万行代码,光靠一个人把关,迟早出问题。我见过太多项目,前期不搞 Review,后期 bug 修到崩溃。

代码审查的核心价值有三点:

  • 缺陷拦截:越早发现 bug,修复成本越低。我统计过,Review 阶段发现一个 bug 的成本,比线上修复低 10 倍不止。
  • 知识传递:新人通过 Review 学习老手的写法,老手也能从新人的视角发现盲区。
  • 代码一致性:确保整个团队的代码风格、架构思路保持一致,而不是各写各的。

核心观点:代码审查不是「找茬」,而是「共同提升」。好的 Review 文化,能让团队的整体代码质量上一个台阶。

Review 流程设计:从提交到合并

流程这东西,太复杂没人愿意走,太简单又形同虚设。我个人习惯用「三阶段模型」:

阶段一:提交前自检

开发者自己先过一遍。我要求团队在提交前做三件事:

  • 运行本地单元测试,确保不破坏已有功能
  • 用 clang-format 格式化代码,统一风格
  • 用 clang-tidy 做静态分析,扫一遍常见问题

这一步做好了,Reviewer 的负担能减轻 50%。

阶段二:Review 流程

提交后,系统自动分配 Reviewer。我建议至少两个人:一个熟悉业务逻辑的,一个熟悉底层架构的。流程如下:

  1. Reviewer 收到通知,开始审查
  2. 提出意见,开发者修改
  3. 循环直到所有问题解决
  4. Reviewer 批准,代码合并

这里有个坑:Review 周期不能太长。我曾经见过一个 Review 拖了两周,结果开发者都忘了自己写了啥。我建议 24 小时内给出初步反馈,48 小时内完成。

阶段三:合并后跟踪

代码合并后,CI 系统自动跑集成测试。如果挂了,自动回滚并通知相关人员。这一步很多人忽略,但其实是最后一道防线。

我的经验:Review 流程要「自动化优先」。能交给机器做的,别让人做。比如代码风格检查、静态分析,这些都应该在提交前自动完成。

Checklist 制定:别漏掉关键点

光有流程还不够,你得有个 Checklist。不然 Reviewer 容易漏掉重点。我整理了一份 C++ 项目的核心 Checklist,分享给你:

类别 检查项 说明
正确性 边界条件处理 空指针、数组越界、整数溢出
正确性 资源管理 RAII 是否用对?有没有内存泄漏?
正确性 并发安全 锁的粒度是否合理?有没有死锁风险?
可维护性 命名规范 变量名是否自解释?有没有魔法数字?
可维护性 函数长度 单个函数不超过 50 行,超过就拆
性能 不必要的拷贝 传参用 const& 还是值?移动语义用了吗?
性能 容器选择 vector 还是 list?unordered_map 还是 map?
测试 单元测试覆盖 新代码有没有对应的测试用例?

这个 Checklist 不是死的。每个项目可以根据自身特点调整。比如做嵌入式开发的,要加「内存对齐」检查;做游戏引擎的,要加「热路径优化」检查。

注意:Checklist 别搞太长。超过 20 项,Reviewer 就会开始敷衍。我建议控制在 10-15 项,覆盖最关键的点就行。

自动化审查工具:Reviewable 与 Gerrit

手动 Review 效率有限,得靠工具。我重点讲两个:Reviewable 和 Gerrit。

Reviewable:轻量级,适合小团队

Reviewable 是一个基于 GitHub 的代码审查工具。它的特点是轻量、易上手。我个人很喜欢它的「逐行评论」功能——你可以直接在代码的某一行上写评论,开发者能精准定位问题。

举个例子:

// 开发者提交的代码
void processData(const std::vector<int>& data) {
    for (size_t i = 0; i < data.size(); ++i) {
        // 处理逻辑
    }
}

// Reviewer 的评论
// 建议用 range-based for 循环,更简洁
// for (const auto& val : data) { ... }

Reviewable 还支持「自动合并」——当所有问题都解决后,代码自动合并到主分支。这个功能很实用,省去了手动操作的时间。

Gerrit:重量级,适合大型项目

Gerrit 是 Google 开发的代码审查系统,很多大型项目(比如 Android)都在用。它的核心特点是「严格的权限控制」和「细粒度的审查流程」。

Gerrit 的工作流是这样的:

  1. 开发者把代码 push 到 Gerrit 的「待审查」分支
  2. Gerrit 自动触发 CI 构建和静态分析
  3. Reviewer 在 Web 界面上审查,给出评分(+1, +2 等)
  4. 达到一定分数后,代码才能合并

Gerrit 的评分机制很有意思:

  • +1:看起来还行,但我不确定
  • +2:没问题,我批准
  • -1:有问题,需要修改
  • -2:严重问题,不能合并

我建议团队设置「至少两个 +2 才能合并」的规则。这样能避免单人决策的偏差。

我的建议:小团队用 Reviewable,大团队用 Gerrit。如果你用的是 GitLab,它的内置 MR 功能也够用。别为了用工具而用工具,适合自己团队才是最好的。

知识体系:代码审查的核心逻辑

说了这么多,我画了一张图,帮你理清代码审查的知识体系:

代码审查知识体系 代码审查 流程设计 提交前自检 Review 流程 合并后跟踪 Checklist 正确性检查 可维护性检查 性能检查 测试覆盖检查 自动化工具 Reviewable Gerrit GitLab MR 目标:缺陷拦截 + 知识传递 + 代码一致性 自动化优先,人工把关关键点

避坑指南:我踩过的那些坑

最后,分享几个我亲身踩过的坑,希望能帮你少走弯路:

  • Review 流于形式:我曾经在一个项目里,Reviewer 只看代码格式,不看逻辑。结果上线后出了严重的并发问题。记住,Review 的核心是逻辑正确性,不是代码风格。
  • Review 范围太大:一次 Review 超过 400 行代码,效果会急剧下降。我建议每次提交控制在 200-300 行,超过就拆成多个提交。
  • 忽视负面反馈:有些开发者收到负面评论会抵触。我见过最极端的情况,两个人因为一个变量命名吵了一周。我的建议是:对事不对人,用数据说话。
  • 工具依赖过度:自动化工具能帮你发现 80% 的问题,但剩下的 20% 需要人来判断。比如「这个设计是否合理?」「这个接口是否易用?」——这些机器看不出来。

总结一下:代码审查不是走过场,而是一项需要认真对待的工程实践。流程要清晰,Checklist 要实用,工具要选对。最重要的是,培养团队的 Review 文化——每个人都愿意花时间帮别人看代码,也愿意接受别人的意见。

好了,这一章就到这里。代码审查这块,说到底就是「人 + 流程 + 工具」的配合。你回去可以看看自己团队的 Review 流程,有没有可以优化的地方。如果有问题,欢迎随时交流。