这次我们直接聊一个偏“流程”但影响面很大的话题:代码审查。无论你现在用 GitLab Review、Gerrit、GitHub Pull Request,还是已经在几个人的小团队里靠口头“帮我看下代码”,本质上你都在做同一件事——在代码进入主线之前,再设置一道人工或自动的质检关卡。
这道关卡最早的标准化形态,叫“费根检查”(Fagan inspection)。1976 年 Michael Fagan 在 IBM 提出这套方法时,代码评审还是一个非常重流程、重文档、重会议的线下动作。而到了今天,AI 代码审查工具开始出现在 CI 流水线里,能自动提意见、给修复建议,甚至直接生成补丁。代码审查从“人肉开会”演变成了“人机协作 + 自动化流水线”的组合体。
这篇文章不是讲某个具体工具怎么部署,而是把代码审查的演进路线、核心环节、AI 能做什么不能做什么、以及如何在团队里落地一套可执行的审查流程系统性地梳理一遍。文中给出的审查清单、CI 检查脚本、AI 接口调用示例都可以直接改到自己的项目里用。适合团队研发负责人、技术管理者,以及想把代码审查从“走过场”提升到“能拦截问题”的开发者。
1. 代码审查演进路线:费根检查到 AI 辅助
代码审查的演进可以按“标准化程度”和“自动化程度”两条线来看。
费根检查是标准化程度最高的代表。它把评审拆成规划、概述、准备、检查会议、返工、跟进六个阶段,每个阶段都有明确的角色分工:作者、主持人、审查员、记录员。检查会议以发现缺陷为目标,而不是讨论修复方案。这套流程在 80、90 年代非常有效,因为它解决的是“代码质量完全靠个人自觉”的问题,用制度约束来保证质量。缺点是太重:一次正式审查需要协调多人时间,准备阶段要读几百行甚至上千行代码,会议要记日志,返工后还要跟进确认。小版本迭代根本跑不起这个流程。
所以后来业界更普遍的做法是轻量化的同行评审,也就是今天最常见的 Pull Request / Merge Request 评审。没有专门的会议,审查员在自己座位上看 diff,写完评论提交,作者回复或修改。这个过程保留了费根检查的核心思想——同行阅读代码找缺陷——但砍掉了流程仪式感。
到了工具化阶段,静态分析工具开始在审查之前“先扫一遍”:编译告警、未使用变量、潜在空指针、安全漏洞模式,这些机械问题不需要人工花时间看。SonarQube、ESLint、RuboCop、golangci-lint 都属于这一类。它们不替代人,但把人从重复劳动里解放出来。
AI 阶段是最近三年的变化。以 GPT 系列为代表的大语言模型开源之后,一批 AI 代码审查工具开始出现。它们做的事情和静态分析不同:能理解代码意图,能对比 PR 描述判断实现是否匹配,能发现命名、结构、边界条件这一类需要语义理解的问题,还能直接生成修复建议。cobot 这类工具的思路就是把 AI 审查嵌入到协作流程里,让机器先审一轮,人再审有争议的部分。
从演进结果看,代码审查没有消失,而是被拆成了三层:
| 层级 | 解决的问题 | 代表方式 | 人力成本 |
|---|---|---|---|
| 规范层 | 代码风格、格式、基础错误 | Static Analysis、Linter、格式化工具 | 低 |
| 语义层 | 逻辑错误、边界条件、结构设计 | 人工评审、AI 辅助审查 | 中 |
| 架构层 | 模块划分、依赖方向、扩展性 | 架构 review、设计评审 | 高 |
费根检查在规范层和语义层之间更偏人工,而 AI 时代的代码审查把规范层完全交给机器,语义层交给“AI 先审 + 人复判”,架构层仍然必须靠人。这是整个演进的核心逻辑:不是 AI 替换审查员,而是审查员的精力向更高层移动。
2. 代码审查的核心环节与技术要点
无论用费根检查、Pull Request 还是 AI 工具,代码审查的底层环节其实没变过。拆开来看是五个步骤。
第一个环节是变更准备。作者把代码改动整理成可审查的形态:提交信息清晰、改动范围合理、相关文档和测试用例齐全。费根检查里对应的是“规划”和“概述”,现代 Git 工作流里对应的是 PR 描述。这个环节做不好,后面所有审查都是在猜。
第二个环节是差异阅读。审查员需要理解 diff 的上下文,知道这段改动处于什么模块、影响哪些调用方、修改前的行为是什么。费根检查要求审查员在会前完成准备,今天的工具则通过 diff 视图、代码跳转、AI 生成的变更摘要来加速这个过程。
第三个环节是缺陷识别。这是审查的核心,也是最依赖经验的部分。常见的分类包括:逻辑缺陷、边界条件、并发问题、安全漏洞、性能问题、可维护性问题。静态分析工具能覆盖一部分,AI 工具能覆盖语义层的相当部分,但最终判断仍然需要人来确认。
第四个环节是沟通讨论。费根检查通过检查会议沟通,现代通过评论、回复、resolve 对话来完成。沟通质量取决于能否给出“问题描述 + 为什么重要 + 建议改法”的完整评论,而不是只写一句“这里有问题”。
第五个环节是跟进闭环。缺陷记录、修改、重新审查、合入。费根检查有专门的返工和跟进阶段,现代工具通过 review 状态、thread 未解决标记、CI 状态检查来强制闭环。
对团队落地来说,前两个环节的自动化收益最大。变更准备可以用 PR 模板和提交规范钩子来约束,差异阅读可以用 AI 生成变更摘要来降低理解门槛。缺陷识别是 AI 介入价值最高的环节,但也是误报率最高的环节。沟通和跟进则必须由流程保证,AI 很难替代。
3. AI 代码审查:能力边界与使用场景
AI 代码审查这两年从“能看懂代码”进步到了“能发现问题并给出修复建议”。但从实践来看,它的能力边界比较清晰。先说能做好的部分。
第一,AI 擅长发现语义层面的低级问题。比如变量判空顺序反了、数组越界、资源没关闭、异常被静默吞掉。这类问题静态分析工具能查一部分,但 AI 能结合上下文判断得更准。
第二,AI 擅长做变更影响分析。给定一个 diff,AI 能把涉及函数、调用链、上下游影响列出来。这一点对新人理解代码、对审查员快速定位影响面很有帮助。
第三,AI 擅长生成修复建议。传统静态分析工具通常只报问题,AI 可以直接给出修改后的代码片段。虽然不一定完全正确,但审查员和作者的确认成本显著降低。
第四,AI 能做变更摘要和审查报告。一个几百行的 PR,人工看完往往需要 20 到 30 分钟,AI 可以先给一份摘要:本次改动核心逻辑是什么、风险点在哪、建议重点看哪几个函数。这能大幅压缩差异阅读的时间。
再说做不好的部分。
AI 难以判断架构层面的合理性。一个模块是应该拆成两个还是合并成一个,依赖方向是否需要调整,这种问题依赖团队的历史背景、业务约束和长期演进方向,AI 只能给泛泛建议。
AI 会产生误报。它经常把“不符合常见写法”当成“错误”,把性能无关紧要的循环优化当成“必须修改”。如果团队不加选择地接受 AI 建议,代码会被改得越来越“像 AI 风格”,而不是越来越“适合团队”。
AI 的安全审查有自己的局限。它能识别明显的硬编码密钥、SQL 注入模式,但面对复杂的业务逻辑漏洞、越权访问这类需要理解数据流和业务规则的问题,准确性远不如有经验的审查员。
所以更合理的用法是:把 AI 当成审查流程里的“第一轮审查员”,负责挡住机械性问题和常见风险;人工负责确认 AI 的发现,并把精力分配到真正需要经验判断的问题上。这也正好对应热词里提到的“cobot”思路——协作机器人,人机协作式审查,而不是全自动审查。
4. 主流代码审查工具与接入方式
代码审查工具目前大致分四类。放在一起看,更能理解 AI 工具在生态里的位置。
| 类型 | 代表作 | 主要能力 | 表现形式 |
|---|---|---|---|
| 代码托管平台内建评审 | GitHub Pull Request、GitLab Merge Request、Gitee | diff 评论、thread 讨论、状态 Check | 平台自带,零额外部署 |
| 静态分析平台 | SonarQube、ESLint、golangci-lint | 规则扫描、圈复杂度、重复代码、安全规则 | CI 集成、质量门禁 |
| 代码评审工具 | Gerrit、Review Board、Phabricator | 严格的分层审查、打分、依赖合入 | 独立服务 |
| AI 辅助审查工具 | Copilot、CodeRabbit、各类 cobot 工具 | 自动摘要、缺陷识别、修复建议、审查报告 | 接入 Git 平台、CI 中运行 |
从接入方式看,第四类是当前的重点。AI 辅助审查工具通常以两种形态接入:第一种是作为代码托管平台的 App 或机器人,在创建 Pull Request 时自动触发审查,把评论发在 diff 上;第二种是作为 CLI 或 API 集成到 CI 流水线,在合并之前把 AI 报告作为检查项。
如果你的团队已经用 GitHub 或 GitLab,最稳妥的落地方式不是立刻引入独立平台,而是先做两件事:一是把静态分析工具的规则收敛到团队自己的规则集;二是选择一个 AI 辅助审查工具,在小范围项目里跑两周,统计误报率和有效建议率。
5. 从零搭建代码审查流程:清单、CI 与 AI 辅助
下面这套流程适用于中小研发团队,不依赖特定平台。核心思路是:建立审查清单 → 本地钩子查基础问题 → CI 做静态扫描 → AI 辅助变更摘要 → 人工聚焦语义和架构问题。
5.1 定义一份能落地的审查清单
很多团队有一种“没有审查重点”的代码审查,靠审查员临场发挥。更好的做法是定义一份精简清单,每个 PR 创建时自动附上。推荐按五个维度设计:
- 逻辑与正确性:是否有空指针、越界、未处理错误?边界条件(空列表、最大值、并发)是否覆盖?
- 安全与合规:是否存在硬编码密钥?是否有注入风险?是否涉及敏感数据的未授权访问?
- 性能:是否存在明显的死循环、重复计算、全表扫描、不必要的大对象持有?
- 可维护性:命名是否清晰?函数是否过长?是否有重复代码?新增依赖是否必要?
- 测试:关键分支是否有测试用例?失败场景是否覆盖?改动是否影响既有测试?
清单格式用 Markdown 表格,放进 PR 模板里。不需要一次检查所有项,而是根据改动类型选择重点。例如纯前端样式改动,重点看可维护性;涉及登录授权的改动,重点看安全与合规。
5.2 提交前本地检查脚本
在提交之前先用脚本挡住低层次问题,能显著减少审查员的无效互动。下面是一个本地预检脚本的示意,按项目情况替换命令:
#!/usr/bin/env bash # pre-commit-check.sh:提交前本地检查 # 实际命令需要按项目技术栈调整 set -e echo "[1/3] 运行代码格式化检查" npm run format:check echo "[2/3] 运行静态检查" npm run lint echo "[3/3] 运行单元测试" npm test这段脚本的本质不是“很复杂的工具”,而是把团队约定转成可执行命令。费根检查里靠会议纪律保证准备充分,今天用脚本保证基础质量。
5.3 CI 阶段静态检查配置
本地脚本只能约束提交者自己,CI 阶段的检查才能约束合入。下面以 GitLab CI 为例,给出一个最小的静态检查阶段配置:
# .gitlab-ci.yml 示例片段,需按实际项目调整 stages: - check lint: stage: check image: node:20-alpine script: - npm ci - npm run lint - npm test rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'在 GitHub 生态里,等价做法是配置必要的 status check。重点是让“合并”这个动作必须经过质量检查,而不是仅停留在仓库规则页面上。
5.4 AI 辅助变更摘要与审查提示词
接入 AI 辅助审查时,最有效的一步不是让它直接告诉你有无问题,而是让它先理解变更上下文,再生成审查视角的摘要。这是因为大模型在“先描述后判断”场景下表现更稳定。
下面是一套可以复用的审查提示词模板,适合在 AI 审查工具或自己封装的接口中使用:
你是资深代码审查专家。请按以下维度审查这次代码变更: 1. 变更目标:根据 PR 描述,判断实现是否匹配目标。 2. 逻辑正确性:找出潜在的空指针、边界条件、异常处理问题。 3. 安全风险:是否可能存在越权、注入、硬编码密钥等问题。 4. 性能风险:是否引入不必要的复杂度或资源消耗。 5. 可维护性:命名、结构、重复代码、职责划分是否合理。 输出格式要求: - 先说变更摘要(3 到 5 句话)。 - 再按“严重 / 中等 / 建议”三个等级列出问题。 - 每个问题必须给出具体文件和行号建议,不能只给泛泛评价。 - 对每个问题给出修复建议代码片段。这套提示词的价值在于把“审查视角”外化成了明确维度。很多 AI 审查效果不理想,不是模型不行,而是提示词没有给出等级划分和输出格式约束。
6. 把 AI 审查能力做成接口服务
如果团队想把 AI 审查能力嵌入到自己内部的代码托管平台或 CI 里,而不是依赖商业工具的 Web 页面,一个常见做法是把模型封装成内部接口服务。
这里给出一个接口调用的通用思路。AI 审查服务通常接收三个输入:变更文件路径、diff 内容、PR 描述。返回结果是结构化 JSON,包含问题列表、等级、修复建议。这属于要按实际工具接口调整的通用模板。
# 以 curl 调用 AI 审查服务的示意(非特定厂商命令,需按实际接口调整) curl -X POST "http://127.0.0.1:8080/api/review" \ -H "Content-Type: application/json" \ -d '{ "files": ["src/auth/login.go"], "diff": "diff --git a/src/auth/login.go b/src/auth/login.go ...", "description": "修复登录接口在用户不存在时的空指针问题", "language": "go" }'Python 侧的调用逻辑可以封装成下面这样:
import requests import json def run_ai_review(diff_text: str, pr_description: str) -> dict: """调用内部 AI 审查服务,返回问题列表。""" url = "http://127.0.0.1:8080/api/review" payload = { "files": ["src/auth/login.go"], "diff": diff_text, "description": pr_description, } # 生产环境需要加超时和重试 resp = requests.post(url, json=payload, timeout=120) resp.raise_for_status() return resp.json() if __name__ == "__main__": sample_diff = """diff --git a/src/auth/login.go b/src/auth/login.go index 1234567..7654321 100644 --- a/src/auth/login.go +++ b/src/auth/login.go @@ -10,6 +10,7 @@ func Login(username, password string) (*User, error) { user := findUser(username) + if user == nil { + return nil, fmt.Errorf("user not found") + } return user, nil } """ result = run_ai_review(sample_diff, "修复登录接口的用户空指针问题") print(json.dumps(result, ensure_ascii=False, indent=2))批量任务的设计思路也类似。团队可以把最近一周的 PR 增量导出成 JSON 列表,逐条调用 AI 审查接口,统计问题密度、按模块归类高频问题。输出结果用一个数据表保存,之后跟踪每个问题的生命周期即可。批量任务要注意的是限流、超时重试、失败隔离,不能因为某一条 diff 格式异常导致整个队列中断。
7. 资源占用与效率观察
代码审查不是纯计算密集型负载,但如果团队自己部署 AI 审查模型,仍然要关注资源消耗。
先说轻量工具。ESLint、golangci-lint 这类静态分析工具在 CI 容器里跑,通常只占几百 MB 内存,执行时间按仓库规模从几秒到几分钟不等。这个问题不大。
重头在于 AI 审查模型。如果团队选择自己部署开源模型做审查服务,所需显存和内存取决于模型参数量。以常见的中等规模模型为例,8GB 到 12GB 显存是一个起点,更小的量化版本可以降低到 4GB 到 6GB,但输出质量和上下文长度会受影响。这里的数字只是通用说法,实际占用需要以具体模型版本和推理框架为准。
对团队来说,真正需要观察的效率指标不是单个请求延迟,而是三个数:单次 PR 审查的平均时间、每千行代码的有效问题数、误报率。它们决定了 AI 审查是“帮人省时间”还是“浪费人时间”。
降低资源占用的常见做法包括:
- 只对新增 diff 行启用 AI 审查,不扫描整个仓库。
- 把大 diff 切分成多个小段,按函数边界提交给模型。
- 在 CI 空闲时段批量跑 AI 审查,避免阻塞合入流程。
- 使用质量门禁分层:静态检查不通过直接拦截,AI 审查结果只做提示,不强制阻塞。
另一个容易踩的坑是端口冲突和进程残留。自建 AI 审查服务时,默认端口可能和团队已有服务冲突,启动失败后占用端口。建议固定服务端口并加入健康检查接口,例如/health。
8. 常见问题与排查方法
代码审查流程中常见的问题,整理成排查表:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 审查流于形式,评论很少 | 没有审查清单和重点,审查员缺乏引导 | 检查 PR 模板、抽查最近 20 个 PR 的评论数 | 把审查清单写进 PR 模板,按改动类型指定审查重点 |
| 静态检查通过的代码仍有严重问题 | 静态规则集过窄,或没有覆盖安全类规则 | 检查当前启用的规则集,确认是否包含安全插件 | 扩展规则集,补充安全扫描项 |
| AI 审查建议大量无用 | 提示词未约束输出格式,或 diff 过大超出上下文 | 查看 AI 审查沉淀的日志和 prompt | 分段 diff,调整提示词,设定严重等级过滤标准 |
| CI 检查时断时续 | 测试用例存在随机失败,端口冲突,资源不足 | 查看失败日志,检查超时设置 | 稳定测试依赖,增加重试机制,单独安排端口 |
| PR 合入后破坏线上功能 | 改动影响面未被识别,缺少回归测试 | 反查该 PR 的 diff 链路和测试覆盖 | 对高风险区域强制补充集成测试,增加变更影响面清单 |
| 审查员过度依赖自动检查 | 团队把质量责任完全交给工具 | 观察人工评论比例是否下降,是否只复制工具输出 | 明确要求人工必须输出对架构/可维护性的判断 |
这里涉及一个国内研发团队经常出现的问题:把“工具跑过了”和“审查通过了”划等号。事实上,静态检查和 AI 审查只能做到“发现常见模式”,无法替你确认“这个模块这样设计在未来三个月里合不合理”。解决办法只有一个——在流程里给人工审查留出不可替代的空间,例如架构评审必须有人工结论,而不是只看机器人是否通过。
如果 AI 审查服务调不起来,优先检查三方面:模型文件是否完整、服务端口是否被占用、输入 diff 是否过大导致超时。
9. 团队落地建议与合规边界
从费根检查到 AI 代码审查,最核心的落地经验不是选哪个工具,而是把“审查”定义成一个有输入、有输出、有闭环的流程。
结合实际团队情况,建议按下面四个阶段推进。
第一阶段是固化基础规范。把格式、Lint、单元测试接进 CI,做到合并前必须通过。这个阶段不引入 AI,先把能自动化的问题自动化。
第二阶段是建立人工评审习惯。要求每个 PR 至少有一位非作者的同事阅读并评论,评论必须有明确结论:问题等级、是否需要修改、还是可以通过。记录每周 PR 评审率。
第三阶段是引入 AI 辅助。挑选一个 AI 审查工具或在内部封装模型接口,要求 AI 评论作为 PR 审查的提示信息,但不作为强制门禁。统计两周有效建议率和误报率,再决定是否扩大范围。
第四阶段是数据驱动改进。定期拉取 PR 评审数据:平均评论数、首次评论响应时间、缺陷密度、复审轮次。找到“反复出现问题”的模块,针对性补充测试用例或重设计。
合规边界也要明确。代码是企业的核心资产,使用任何 AI 代码审查服务时都建议先确认:数据是否离开内部网络、模型厂商是否有数据留存、审查结果是否会被用于模型训练。涉及商业机密、支付逻辑、内部基础设施代码时,更稳妥的做法是使用私有化部署的模型。这一点在团队决策时必须讲清楚,否则后续会带来很大的数据合规风险。
同时,对 AI 生成或 AI 修复的代码,要建立二次确认机制。AI 给的修复方案不一定正确,合入前必须由作者或审查员确认修复片段,不能只图“AI 说过”就合入。
10. 总结与下一步
代码审查演进到现在,可以概括成三句话:费根检查证明了“有组织的同行评审能显著降低缺陷率”,工具化阶段把机械劳动从人身上拿走了,AI 时代则把“第一轮审查”从人转移到了模型。但每一层都保留了一个共同事实——最终对代码负责的是人。
这篇文章里最值得马上动手验证的是两件事:一是检查你现在项目的 CI 里,合并前是否真的有静态检查和测试门禁;二是用一个规模不大的 PR,让 AI 生成一份变更摘要,和人工审出来的重点对比,看有效建议占比是多少。
最容易踩的坑是跳过基础检查直接上 AI。如果 Lint、单元测试、人工评审都没跑起来,AI 审查只会在混乱之上叠加另一层噪声。
后续可以继续扩展的方向有几个:一是把 AI 审查结果接入消息通知,让作者在 PR 创建后立刻看到 AI 报告;二是做审查数据的统计报表,按模块、按人、按时段看问题密度;三是把审查能力做成内部 API 服务,接入自己的代码托管平台。对于参与型开源项目或其他偏治理形式的项目,这套思路同样适用,只需要在流程上做轻量化裁剪。
代码审查没有终点。费根检查解决的是“没人按流程审”的问题,今天的 AI 工具解决的是“人没时间审”的问题,下一步我们真正要解决的是“审完之后团队有没有真的记住”的问题。
建议收藏备用,拿这篇文章里的审查清单和 CI 示例,先把你自己的项目流程补齐。