Code Review 与复杂性预算:管住 AI 代码的出口

Vibe Coding; Code Review; 复杂性预算; 技术债; 代码质量 2930 字 15 min read

前一篇讲的是入口——用结构化的 Prompt 决定 AI 往哪个方向生成。这一篇讲出口

已经把代码写出来了,你凭什么决定它能不能进你的代码库?

两头一卡,中间的质量才稳得住。入口靠 Prompt,出口靠 Code Review,而判断”进还是不进”的那把尺子,叫复杂性预算。

效率这个词需要重新理解

先破一个迷思。

AI 把写代码的速度拉高了。过去半天才能搭好的页面,现在十分钟搞定;过去对着文档查两小时的 API 对接,现在一句话丢过去就能拿到初稿。产出量涨了,这没什么好争的。

但有一个越来越强的感受:产出量涨了跟系统质量涨了,是两回事。 把这两件事混在一起,会出大问题。

做过项目的人对下面这个过程不会陌生。刚开始推进飞快,模块少,边界清楚,想怎么改就怎么改,很爽。但系统长到一定规模,情况就变了

,命名开始漂移,早期图省事留下的妥协固化成了结构惯性。团队投入没减少,人手可能还多了,但大家开始觉得”改不动了”。一个小改动要摸清楚它会影响哪些地方,光这件事就够累的,动手改反而是小头。

AI 没有改变这个过程,只是把它调快了。

在传统开发里,人手写代码本身就是一层天然的过滤器。你写得慢,草率的依赖、混杂的实现,扩散速度就是有限的。但 AI 协作环境不一样

,几轮生成之后就成了整个代码库默认的写法。后面的人看到前面这么写,不会多想;AI 也照着这个模式继续补。“不太好的做法”很快就变成了”项目的标准做法”,而且这个过程是静悄悄发生的。等你意识到的时候,已经改不动了。

AI 是一个忠实的放大器。你走对了路,它帮你加速;你走错了路,它也帮你加速,速度一样快甚至更快——因为走错路的时候你不会停下来仔细想。

所以现在看”效率”这个词要谨慎很多。“单位时间写了多少行代码”这个指标,在 AI 时代没有意义。工程上的效率得看更长的时间窗口

,结构还能不能承受变化,加新东西的时候有没有余地。如果你每次交付都在透支可维护性,那这种效率只是在借未来的债。

开发者当中流传一句杀伤力很大的话:“先这样吧,后面再改。” 但回头看,“后面再改”在大多数项目里没有一次自动发生过。每次都是等到问题大到不得不改,才发现改的成本比当初做对的成本高了好几倍。技术债拖得越久,利息越高。

复杂性预算

“复杂性预算”这个概念很少有人提,但做过大项目的人一定有体感。

一个项目在特定阶段内能承受的结构复杂度是有上限的。复杂性本身消除不了——任何系统都要处理状态、边界、异常、依赖和演化。你能管的,是复杂性进入系统的速度、位置和规模

AI 让这个问题变得格外尖锐。AI 可以源源不断地给出”看起来可用”的实现,团队只要缺少筛选机制,技术债就会以前所未有的速度累积。看似在往前跑,实际在往坑里滑。

所以要设一道关口

代码进入系统前,至少要过四道检查。

可读性。 维护者能不能在短时间内说清这段代码在干什么?一种很常见的情况

,测试也过了,但问作者”这段逻辑为什么要这么写”,他自己也说不清楚。这种代码进了主干就是定时炸弹。说不清的,先重写再合并。

职责边界。 每个函数和模块是不是只对应一个主要任务?那些跨层级堆叠、一个函数干五件事的实现,优先拆。

重复控制。 同一个规则是不是被复制粘贴到了三个地方?如果是,预算就在泄漏。AI 特别喜欢复制局部逻辑来快速满足需求——金额格式化在 A 文件写一遍,B 文件又写一遍,C 文件再来一遍,每个版本还不完全一样。

架构服从。 新代码进入系统时,在现有结构里有没有明确的位置?”写得顺手”不能成为偏离主线的理由。

还有几个信号一旦出现就要警惕:

  • 同一个需求的改动需要跨越太多文件,找不到主修改点
  • 新增功能必须先修补一堆历史遗留问题才能落地
  • 一次改动反复引入非预期回归

出现这些情况,说明局部复杂性已经超标了,再叠功能只会更糟。

预算不是均匀分配的。 核心业务模块和高频变更路径要保持更低的复杂度上限,外围适配层可以容纳一定的技术复杂度——前提是边界清楚而且可替换。不少项目把复杂度堆进了核心区,最后整个改不动,只能推倒重来。

Code Review 变成了第一基本功

过去 Code Review 是开发流程里的一个环节。现在,它正在变成编程活动的第一基本功

原因很直接

几分钟就能产出数百行代码,但团队要对这些代码承担长期责任。代码一旦合入主干,后续每一次迭代、排查、调整都会和它发生关系。AI 产出速度快,但你必须保留对每一行代码的解释权和否决权。

AI 时代的 Code Review 至少要覆盖四个维度:

正确性。 最常见的坑是”主路径能跑,但边界条件一塌糊涂”。AI 根据”贪吃蛇”这三个字凑出一个能跑的东西,但”连续按方向键会不会 180 度掉头撞死自己""窗口 resize 会不会重置状态”这些边界,它不会主动想。审查时重点盯边界。

可读性。 AI 有时候会用一些”技巧性写法”压缩长度,看起来简洁,实际上把理解成本甩给了未来的维护者。简洁 ≠ 可读。

可维护性。 盯住重复逻辑。前面说的”金额格式化写三遍”就是典型——AI 为了快速满足当前需求,倾向于复制而非抽象。

可演进性。 AI 有时候为了满足当前任务引入过重的依赖或过度的抽象,短期看很完整,长期代价极高。多问一句:“这个依赖/这层抽象,是这个任务真的需要,还是它顺手加的?”

一个比较稳妥的 PR 评审流程可以分五步:

  1. 需求对齐——这个 PR 到底要解决什么?和 Spec 对得上吗?
  2. 结构先行——先看它改了哪些文件、动了哪些边界,再看具体实现
  3. 证据驱动——不听”应该能跑”,看测试、看日志、看实际运行结果
  4. 问题分级——区分”必须改”(阻塞合并)和”可以后续优化”(记 issue),不要什么都卡
  5. 复审合并——改完再看一遍,确认没引入新问题

这个流程不复杂,但能有效降低那种”语法全对但架构歪了”的情况。

AI 编程时代里,你缺的不是能写代码的工具。你缺的是能看出问题的眼睛。

用测试给重构兜底

Code Review 是人工判断,但有一个能力可以让 AI 自己把代码质量往上抬,前提是你给它搭好安全网。

这个能力可以叫非退化性

,不会把正常代码误判为问题,同时能准确识别出真正有问题的代码。

听起来平平无奇,但它能推出一个很有用的结论。假设模型具备非退化性:

  1. 单次审查——带着某个具体问题(比如”有没有重复逻辑”)审查代码,AI 总能找到可以改进的地方
  2. 多次迭代——每次修改后,该类问题的出现次数递减
  3. 收敛——经过多轮审查,代码在这个维度上的问题趋近于零,对 AI 来说变得”平滑”,不再有突兀之处

进一步,如果每一个问题的解决都不会引入新问题,那么:预设足够多维度的重构清单,反复让 AI 带着相同的问题去重构,最终代码就会在所有这些维度上达标。

关键在于”每次修改不引入新问题”这个前提怎么保证——答案是测试集

操作方法:

  1. 构建安全网
  2. 预设问题清单
    AGENTS.md / CLAUDE.md 里列出重构维度(SOLID 原则、分层清晰、防御式编程、无重复逻辑……)
  3. 迭代优化
    AI 对照清单逐项优化

因为有测试兜底,任何破坏逻辑的修改都会让测试跑不通,AI 必须始终保持测试可以跑通。于是每一次重构都建立在”质量不会降低”的基础上,非退化性就能保证代码始终朝变好的方向迭代。

这也顺带回答了一个问题:是不是必须用最强的模型才能写出好代码? 不一定。理论上,一个中等模型只要”会做题”、稍微会写代码,靠反馈循环 + 非退化性,也能逐步逼近好代码。更强模型的价值在于更高效地做题——对复杂问题建模、根据长上下文和日志分析定位问题。分析能力有限的模型遇到硬 bug 会陷进去反复无效思考,而不是”写不出好代码”。

小结

  • 效率要看长窗口
    ≠ 系统质量,AI 是放大器,走错路它也帮你加速
  • 复杂性预算
    ,但你能管它进入系统的速度、位置、规模;核心区上限要低,外围可以宽
  • 四道关口
    、职责边界、重复控制、架构服从——过不了就别进主干
  • Code Review 是第一基本功
    (盯边界)、可读性、可维护性(盯重复)、可演进性(盯过度依赖)
  • 非退化性 + 测试兜底
    ,让 AI 自己把质量收敛上去

下一篇讲一个更上层的判断

AI 尽情 Vibe,什么时候必须切回工程纪律——探索区与生产区的边界。


本文主要参考:《Vibe Coding 到底是什么》(blog.byebug.cn)§06 复杂性预算、§08 Code Review;以及《一套简单有效的 Vibe Coding 实践》中的”非退化性”论证(linux.do)。