同一个 PR,两种不同的 review
我认识的一位开发者,把 Claude Code 设成 CI 里的代码 reviewer。「就让 Claude 帮我看 PR,」他对我说,「它会抓到一些我可能漏掉的东西。」我请他把同一个 PR 丢给 Claude 跑两次。
第一次 review 说 error handling 看起来很完整。第二次 review 却说它不够完整,还提出三个修改建议。他盯着屏幕看了五分钟,搞不清楚到底哪一个 Claude 才是对的。
两个都不算对,也两个都不算错。 LLM 做的是概率性的猜测,不是在套用确定性的规则。而这正是用 AI 做 verification 的核心问题:verification 需要两个 LLM 没有的特性。确定性,也就是相同输入永远产生相同输出。完备性,也就是所有 failure mode 每次都能被抓住。
确定性的问题
把同一段代码丢给 Claude Code 跑两次,你可能会得到两份不同的 review。Temperature、context window、prompt 的措辞——这些都会引入方差。对 brainstorming 来说,方差是 feature。但对 verification 来说,方差就是 bug。你不能在概率性的质量关卡上构建可靠的软件。
确定性检查没有这个问题。tsc --noEmit 永远输出同样的错误。ESLint 在同一套配置下永远标记同样的问题。这些工具套用的是规则,不是概率。它们很无聊。正因如此,它们才管用。
O(1) Guard Stack
下面是我们的 pre-commit stack。每一项检查都是确定性的。每一项都在毫秒级完成。没有哪一项在乎代码是人写的还是 Claude Code 写的:
# Pre-commit (< 1 second total)
TypeScript strict mode # tsc --noEmit
ESLint boundary rules # module isolation
Import restrictions # dependency direction
dependency-cruiser # graph analysis
Unit tests # contract verification
# CI (< 30 seconds)
Full test suite
Bundle size checks
Fresh clone launch test
关键指标是相对代码库规模的 O(1)。类型检查是增量的。Lint 是按文件的。dependency-cruiser 线性地分析 import graph。它们都不会因为你的 vibe-coded 应用变大而变慢。Claude Code review 则相反——代码库一旦超出 context window,就会越来越慢、越来越不准。
确定性检查到底能抓到什么
上周,dependency-cruiser 抓到一个 PR:一个 screen 组件直接 import 了 service 的实现,而不是走 barrel。那个 PR 编译没问题,测试也全过。Cursor 自动生成了那行 import。但它违反了我们的架构规则:只有 composition root 可以 import 实现。
LLM review 也许能抓到这个。也许抓不到,因为那个 PR 很大,import 埋在文件中间。dependency-cruiser 永远不会漏掉,因为它不是在「读」代码——它在分析一张图:
// .dependency-cruiser.cjs
// 8 lines. 100ms. Catches this every time, forever.
{
name: 'no-deep-service-imports',
comment: 'Only service barrels are public.',
severity: 'error',
from: { pathNot: '^src/services/' },
to: {
path: '^src/services/[^/]+/',
pathNot: '^src/services/[^/]+/index\.ts$',
}
}
八行代码。不到一百毫秒。每个 PR。永远。Claude Code review 花 token、花时间,而且可能漏掉。这笔账根本没得比。
AI 真正该待的位置
我不反对 AI。我每天都在用 Cursor 和 Claude Code——写草稿、探索方案、写测试、调 bug。但我不把它们放在 verification 的角色上。Verification 必须是确定性的。必须无聊。必须每次都一模一样。
Guardrails 应该是隐形且不可避免的。它们应该在没人记得要跑检查的时候依然能抓住问题。这就是确定性检查给你的东西:一张永不休息、永不疲倦、永远不会随着代码库增长而丢失上下文的安全网。
Autotomy Expo Starter Pack 自带 TypeScript strict mode、ESLint boundary rules、dependency-cruiser 配置和 pre-commit hooks——全部预配置好。你用 Cursor 的速度来生成代码,用机器的精度来做验证。这才是不搞崩生产环境的 vibe coding 方式。