@garrytan: https://x.com/garrytan/status/2054064931515855118
摘要
Garry Tan 认为,Claude Code 和 Codex 等 AI 编程代理通过使高测试覆盖率变得经济可行,改变了软件工程领域。这创造了一种“复杂性棘轮效应”,确保代码质量在牺牲速度的前提下随时间推移而不断提升。
查看缓存全文
缓存时间: 2026/05/12 07:25
AI 代理复杂性棘轮:为何需要 90% 的测试覆盖率
过去一年里,我一直在使用 AI 进行编程。不仅仅是写提示词——而是在构建真正的软件。我有两个开源项目:GStack,旨在提升 AI 编程代理的能力;以及 GBrain,它将你阅读和撰写的所有内容转化为 AI 可用的可搜索知识库。这两个项目加起来约有 97 万行代码和 665 个测试文件。绝大部分代码都是在我的指挥下由 Claude Code 和 Codex 编写的(大多数时间同时运行 15 个 Conductor 会话)。
上周,我在 72 小时内合并了 14 个拉取请求(Pull Requests)。新增了近 29,000 行代码。每一次发布都比前一次拥有更完善的测试。
这原本被认为是不可能的。速度和质量之间通常存在权衡。快速发货就会破坏东西,慢工出细活才能交付完美产品。二者选一。
现在你不再需要做出这种取舍。解锁的关键是 90% 的测试覆盖率——而 AI 代理让达到这一目标变得零成本。在过去五十年里,维持这种级别的验证需要消耗太多的人类意志力。现在,代理在编写代码的同时也编写测试。其结果就是我所说的“复杂性棘轮”:一个只能变得更好、绝不会变差的系统。
(这是关于使用 AI 构建软件的系列文章中的第七篇:1 2 3 4 5 6)
软件曾经很脆弱
在过去五十年里,软件工程这门学科整体上是围绕一个理念构建的:防止错误,因为错误是灾难性的。
你必须第一次就把代码写对。漏掉一个边界情况,生产环境就会崩溃。部署一个糟糕的数据库迁移,就会丢失客户数据。编写一个功能晦涩的函数,当唯一理解它的人离职后,就没人知道它为什么能工作。整个系统依赖于人类的细心,而人类并不细心。因此,我们构建了复杂的流程——代码审查、预发布环境、质量保证团队、发布周期——所有这些旨在在错误到达用户之前将其捕获。
这有点效果。但它很慢。而且这意味着任何软件系统的复杂性都有一个硬性上限:即一个团队能同时保持在脑海中的事项数量。
如今软件变得“柔软”
我指的并不是马虎。我指的是一种以前不可能实现的韧性。
当我說“模型时代已来”时,我的意思是 AI 编程代理——Claude、GPT、Codex 以及围绕它们发展的生态系统——现在能够阅读代码、理解上下文、诊断错误并编写修复方案。虽然不是完美的,但已经足够好,使得软件错误的模型发生了改变。
迁移失败?代理读取错误消息,理解跨越 45 个版本的数据库模式历史,编写修复代码,编写测试。文件同步在数百万个符号链接上挂起?代理诊断出解析器超时,将其限制在 30 秒内,带着测试一起发布修复。提取管道存在归因错误?跨模型评估捕捉到了它,提示词经过迭代,在数据库层面增加了强制执行。
对于大多数代码级别的错误——逻辑错误、解析失败、破碎的边界情况——代理现在可以在下一轮交互中诊断并修复它们。这是真正的创新。仍然具有灾难性的错误是那些破坏状态性的错误:在生产数据上的错误迁移、被利用的安全漏洞、无法收回的隐私泄露。棘轮机制在这方面也有帮助(良好的测试能在到达生产环境前捕获其中大部分),但真正的转变在于,代码库中绝大多数的错误都是可修复的类型。
这是软件构建方式的一次相变。但这只有在具备“棘轮”的情况下才有效。
代理复杂性棘轮
棘轮是一种只允许单向运动的机械装置。套筒扳手可以将螺栓向前拧紧,并防止其反向松开。这就是这个比喻的含义。
在由代理编写的软件中,每一次与 AI 代理的编程会话都会向代码库中添加三样东西:
-
测试:编码了“正确”的含义——每次有人更改代码时都会运行的自动化检查,如果更改破坏了某些东西,它会大声报错
-
文档:记录决策的原因——不仅仅是代码做了什么,还包括背后的推理和权衡
-
评估结果:确立质量阈值——对输出质量的结构化评估,带有评分,因此你知道下一个版本是更好还是更差
下一次代理操作代码库时,它会将这三者全部加载到其上下文窗口中(AI 可以看到并进行推理的文本)。它无法低于测试套件的标准——否则测试会失败。它不能忽视文档——文档就在上下文中。它不能交付低于评估基线的质量——评分已被记录。
质量底线随着每一次交互而提高。只进不退。这就是棘轮。
实践中的样子
让我们具体化一下。GBrain 是我正在构建的一个知识系统——它通过存储、索引和搜索个人的笔记、会议、对话和研究,为 AI 代理提供长期记忆。可以将其视为你的 AI 助手真正能读取的“第二大脑”。
其中一个功能是认识论提取(epistemological extraction):它浏览成千上万个页面,提取谁在什么时间以多大的信心相信什么。“Garry 认为比特币会达到 30 万美元(信心:0.45)。”“Jared 认为这家初创公司有强劲的留存率(信心:0.80)。”诸如此类,但跨越 28,000 个页面。
第一次提取运行抽取了 100,720 个主张。我使用跨模型评估来评定质量——我让 GPT-5.5 和 Claude 独立对输出进行评分。总体评分:10 分满分中的 6.8 分。
最大的问题是什么?我称之为“持有者混淆”。以主张“到 2027 年,AI 将取代 80% 的软件工程师”为例。谁持有这一信念?是写它的人吗?是他们引用的人吗?还是系统的分析引擎,它从播客转录中推断出的?版本 1 有 35% 的时间搞错了这种区别。这很重要——如果你正在构建一个追踪人们相信什么的系统,你需要知道谁相信它。
因此,评估结果被文档化了。确定了六种具体的失败模式。版本 2 的提示词解决了所有这六种问题。权重舍入(信心分数)在数据库层强制执行——不再有像 0.74 这样的虚假精度,当 0.75 才是诚实的答案时。十七个测试锁定了契约。
现在,未来的任何提取版本在没有通过那 17 个测试的情况下都无法发布。没有人需要记住为什么权重舍入很重要,或者什么是持有者混淆。测试记得。
质量底线永久提高了。这就是棘轮的一次转动。
为什么大多数“氛围编码”项目会消亡
“氛围编码”(Vibecoding)是 Andrej Karpathy 的术语,指通过自然语言描述你想要的东西并让模型生成代码来使用 AI 进行编程。它很强大,这也是我构建项目的方式。但从我在 YC 申请和开源仓库中看到的情况来看,大多数跳过测试的“氛围编码”项目一旦达到中等复杂性——几千行代码,几个交互功能——就开始分崩离析。
它们跳过了棘轮。没有测试,没有文档,没有评估。代理增加了复杂性,但没有任何东西阻止退步。每个新功能都有破坏旧功能的风险,而没有测试,直到用户报告你才会发现。到版本 0.5 时,代码库变成了一个鬼屋,每次更改都会破坏一些意想不到的东西。然后开发者写了一篇博客文章,说 AI 编程行不通。
AI 编程本身工作得很好。他们只是没有构建棘轮。
你可以争辩说,编写测试的人也往往是那些一开始就编写良好架构的人。没错。但棘轮机制与个人无关——它与下一次交互发生什么有关。当新的贡献者打开拉取请求,或者模型版本发生变化,或者你在凌晨 2 点编码且判断力受损时,测试会捕获退步,无论是谁编写的。即使人类状态不佳,棘轮也能工作。这就是重点。
没有测试,改进是一个充满噪声的过程——代理试图让事情变得更好,但没有退步信号,好的更改和坏的更改同样不可见。有了密集的测试套件,你就能在测试覆盖的表面上获得棘轮:对于你编码的行为,质量只能上升。这只是系统的大部分,不是全部。但足以以高速维持前进的动力。
测试作为制度记忆
在传统软件公司中,制度记忆存在于人类身上。知道为什么存在那个缓存层的高级工程师。记得那次差点摧毁数据库的迁移的架构师。能够解释计费系统中奇怪边界情况的技术主管。
人会离开。他们退休,被挖角,精疲力竭。当他们离开时,知识也随之而去。每家软件公司都有过这样的经历:打开一个关键文件,发现评论写着“// 不要更改这个——去问 Dave”,而 Dave 三年前就离开了。
代理的上下文窗口不会离职。它不会被挖角。它不会忘记。当测试套件编码了“权重舍入必须使用 0.05 的增量”,而文档解释了“因为跨模态评估显示虚假精度会降低对信心分数的信任”,这种知识就是持久的。任何代理,任何模型,在任何时候都可以加载该上下文并理解约束。
测试是能够抵御员工流动的制度记忆。对于单人项目来说,它们甚至更加关键——它们是你唯一的制度记忆。
一切可利用的都是可测试的
棘轮不仅适用于传统代码。它适用于计算机可以观察的任何事物。
想想现代系统的层次。操作系统为你提供进程树、文件系统状态、网络套接字、cron 调度。终端提供每个按键、每行输出、每个交互提示。浏览器为你提供渲染的页面、按钮状态、导航事件。API 为你提供可解析和验证的结构化响应。而 AI 代理为你提供可观察的行为——它们说了什么,调用了什么工具,做事的顺序,以及它们在行动前是否询问。
所有这些都是可利用的。如果你能利用它,你就能观察它。如果你能观察它,你就能对其断言。如果你能断言它,你就能棘轮它。
这比传统的单元测试具有大得多的表面积。让我展示给你看。
GStack 是我的开源编码代理框架——拥有 93,000 个 GitHub 星标,701,000 行代码,46 项技能。其核心功能之一是交互式计划审查:你让它审查你的架构,它逐节浏览计划,提出问题,探测边界情况,挑战你的假设。就像拥有一个真正阅读代码的工程经理。
问题在于:Claude Code 有时会跳过整个交互式部分。它会读取计划文件,一次性倾倒所有发现,然后退出——没有向用户提出任何问题。审查的整个重点在于来回对话。跳过它就失去了意义。
你甚至如何测试这一点?你不能单元测试“AI 是否进行了对话”。没有传统的测试框架涵盖这一点。
所以我使用 Bun 的 TTY 功能构建了一个测试夹具(PR #1354),它实际上在伪终端中生成 Claude Code,向它提供特定的仓库场景,触发审查技能,并实时观察终端输出。该测试观察代理在完成前是否发出交互式问题。如果它倾倒发现并退出而不问任何问题,测试就会失败。
这不是测试代码。这是测试 AI 代理是否遵循行为契约。在 TTY 级别。通过字面意义上的观察其工作。
棘轮响应分为三层:
-
技能指令中的 STOP 门控——明确的规则,说明“在继续下一部分之前,你必须询问用户”,并带有反合理化条款,命名特定的失败模式,以便模型不能说服自己跳过
-
反捷径条款——“计划文件是交互式审查的输出,而不是它的替代品。”一句话,关闭了模型不断利用的确切漏洞。
-
门控层底线测试——在受控场景中生成 Claude Code 的 TTY 夹具测试,如果代理没有提出至少一个交互式问题,则测试失败
现在,当 Anthropic 发布新模型版本,或者我更改技能提示词时,测试套件会捕获交互式契约中的任何退步。代理不能默默地停止提问。测试观察终端并检查。
或者以 PR #880 为例,它发布了一个新的 OpenClaw 插件。测试不仅检查代码是否编译。它从源代码构建插件,在隔离配置文件中生成真实的 OpenClaw 实例,通过 CLI 安装插件,运行 plugins inspect 以验证运行时是否加载了它,设置配置槽,验证配置,并运行 plugins doctor 以确认零诊断错误。跨越两个单独程序的完整端到端往返。359 行测试代码。人类几乎不会手动编写的那种测试,因为设置太繁琐了。Claude 在大约五分钟内编写了它。这就是努力壁垒在实时消失。
这一原则具有普适性。你可以在操作系统级别进行测试:迁移是否创建了正确的表,cron 作业是否触发,进程是否仍然存活?在浏览器级别:页面是否渲染,代理是否正确填写了表单。在 API 级别:模型是否返回了具有正确模式的有效 JSON。在行为级别:代理是否遵循协议,删除前是否询问,被告知停止时是否停止。
整个堆栈都是可测试的。棘轮适用于所有这些。大多数人尚未意识到这一点,因为他们仍然将测试覆盖率视为“我的函数是否返回了正确的数字”。真正的测试表面是计算机能看到的一切。
90% 这个数字
那么,90% 的测试覆盖率实际上为你带来了什么?
Capers Jones 研究了超过 10,000 个软件项目,并测量了缺陷移除效率(DRE)——即在到达用户之前捕获的 bug 百分比。他来自 Applied Software Measurement 的数据显示了一条非线性曲线:在低于 70% 的覆盖率下,DRE 保持在 65-75% 左右。在 85-95% 的覆盖率下,DRE 跃升至 92-97%。这种关系不是线性的。在 85% 左右有一个曲线拐点,缺陷逃逸率在此急剧下降。
航空电子行业几十年前就明白了这一点。DO-178C 是 FAA 针对飞行关键软件的标准,要求 Level A 系统(那些 bug 意味着飞机坠毁的系统)具备修正条件/决策覆盖率(MC/DC)。仅分支覆盖率会遗漏 10-20% 的故障。MC/DC 比行覆盖率更严格,可实现 >99% 的 DRE。他们强制要求这一点并不是因为官僚喜欢文书工作。他们强制要求是因为数据显示,低于某些覆盖率阈值时,关键缺陷的逃逸率与不致死人的要求不相容。
可靠性工程的类比很清晰。工厂使用一种称为六西格玛的系统来衡量质量。其理念是:计算每生产一百万个单位产生的缺陷数量,然后将其表示为“西格玛水平”——西格玛越高,缺陷越少。3 西格玛过程每百万产生约 67,000 个缺陷(相当糟糕)。4 西格玛过程产生约 6,200 个(好十倍)。5 西格玛过程产生 233 个(又好 27 倍)。从 4 到 5 西格玛的跳跃不是渐进式改进。它是相变。
测试覆盖率遵循相同的曲线。从 70% 到 90% 的覆盖率并不是好 30%。它意味着逃逸缺陷的数量减少了一个数量级。在 70% 覆盖率下溜走的缺陷隐藏在未测试的 30% 代码中。在 90% 覆盖率下,藏身之处缩小到 10%,大多数危险路径被锁定。
现在,我应该诚实地指出研究也显示的内容。Mockus、Nagappan 和 Dinh-Trong 研究了 Windows Vista,发现虽然覆盖率与发布后缺陷减少相关,但达到 90%+ 覆盖率的努力急剧上升。最后
相似文章
@SaitoWu: https://x.com/SaitoWu/status/2053101671035851216
The article summarizes a talk by Matt Pocock criticizing 'specs-to-code' approaches, arguing that solid software engineering fundamentals like TDD and modular design are more critical than ever for effectively using AI coding assistants like Claude Code.
@garrytan: 重点不在于 AI 让你写代码更快。很多人已经注意到了这一点。真正在于的是,AI 让你能够在以前因成本过高而无法持续的层级上进行验证……
该帖认为,AI 在编程中的核心价值不仅在于更快地编写代码,更在于实现可持续的高层级验证和测试,而这在过去需要耗费过高的人力成本。
@rohit4verse:AI 并没有让代码变得廉价,而是让劣质代码变得致命。Matt Pocock:“软件基础比以往任何时候都更重要”AI 在……
探讨了 AI 如何放大代码质量的影响,强调软件基础比以往任何时候都更重要,并推荐了构建可靠 AI agent 的五种设计模式。
@NainsiDwiv50980:“我觉得自从12月以来,我连一行代码都没写过。”当 Andrej Karpathy 说这话时,大多数人将其视为……
Garry Tan 推出了 'gstack',这是一个开源工具,用于编排 AI 智能体以充当完整的软件团队。他声称,通过从编写代码转变为指挥 AI 系统,开发产出提高了 810 倍。
@garrytan: 我的朋友 @finbarr 说:这就像代码即记忆。你以非确定性的方式与你的智能体协作,摸索出如何...
Garry Tan 探讨了 AI 智能体“代码即记忆”的概念,建议它们为新任务生成可执行脚本,并重复利用以提高效率。