AREX Feed Article
Anthropic 自报:Claude 撰写公司 80% 代码,CI 任务半年增至 25 倍,测试选择服务三次修补后被重写
2026 年 9 月 14 日,Anthropic 发布工程博客文章,用一组内部数字描述 agentic coding(由 agent 驱动的编码方式)给持续集成(CI)带来的压力:Anthropic 自报 Claude 撰写了公司 80% 的代码,CI 任务量在六个月内增至 25 倍。这些数字均出自 Anthropic 内部统计,未经独立核实。
文章的另一条主线,是测试影响分析(test impact analysis,也作 test selection)服务:它决定「每次改动该跑哪些测试」,也多次被这轮增长推到过载边缘。作者 Sachin Malhotra 写道,团队先后为它打了三次快速补丁,每次争取到的时间都比上一次更短;最终服务被整体重写,每条测试的历史记录从单进程内存搬进内存数据存储,才换来水平扩展能力。
写代码不再是约束,压力传到评审和 CI
博文给出的对照基线是 2021~2025 年:Anthropic 工程师人均每季度交付的代码量现在达到这一基线的 8 倍,代码库测试总量增至 10 倍,而新增的工程师人数很少。文章的判断是,写代码不再是约束:一旦 PR(合并请求)的评审也被 agent 加速,CI 就开始吃紧。作者预计,随着使用 agent 的团队同时产出更多 PR 和更多测试,水平扩展的测试选择架构会成为行业标准。
文章还提醒,任务量的增长并不意味着每个 PR 都会运行全部测试;这也正是测试影响分析服务要解决的问题。
listener 落后 20 分钟,数万条测试结果进不了 selector
据博文介绍,Anthropic 自己构建了一套确定性的(deterministic)测试影响分析服务:根据历史表现和 package(代码包)相关性,决定每次改动要运行哪些测试。博文称这类做法并不罕见,市面上也有一类厂商提供类似的产品。
服务依赖两个必须保持同步的部件:负责记录每次 CI 运行测试结果的 listener(监听进程),以及读取结果历史、决定每个打开的 PR 跑哪些测试的 selector(选择器)。Malhotra 提到,他的许多同行所在的组织仍在每个改动上运行全部测试;这在某个规模点之前可行,之后 CI 门禁会变得越来越长、越来越贵、越来越不可信。
当每秒都有多个 CI 任务运行时,listener 开始跟不上 PR 队列。博文举了一个例子:20 分钟的 listener 滞后,可能意味着数万条测试结果更新没有写进 selector。博文把这类滞后可能造成的后果归为三类:坏改动被合并后,某个测试会开始对其他所有人失败,引发多轮不必要的排查;某个依赖开始变得不稳定时,时好时坏的失败会挡住合并;测试被修复或新增后,在 listener 追上之前不会运行,带来回归风险。
作者还观察到一个差别:人类很擅长判断哪些测试失败与自己无关,而 agent 需要更明确的上下文与指令;当 agent 拿到一组具体、有效的测试时,自我验证和迭代会顺畅得多。
结构性问题在于,整套逻辑跑在单个进程里:要维护每条测试的运行历史,就必须由单一写入者来应用结果。这个 v0 设计使服务无法水平分片。
更大的机器、进程内分片、每日重启
2025 年 10 月,服务已经出现吃紧迹象,团队连续两天收到告警呼叫。
第一次修补是把运行这个服务的核数翻倍。作者当时就知道这不会持久,事实也是如此:这次修补只买到 70 天。
第二次修补是进程内分片。在第一次修补之后,listener 的滞后频繁触发告警。为推进更长期的修复,Malhotra 在内部版本的 Claude Tag 里开了一个长期会话专门监控这个服务:只要 listener 落后超过 50,000 个任务,Claude 就会提醒他,继续讨论下一步。这段协作持续了数月,也省去了反复向它复述背景的麻烦;Claude 经常主张做一次大修,但团队通常还是选择再打一个补丁。2026 年 2 月,CI 任务的指数增长再次施压,他们决定并行化:把写入者从「整个服务一个」改为「每个 package 一个」;Claude 生成了把各 package 状态拆成独立分片与 worker(工作进程)的代码。这次修补只买到 29 天。
第三次修补是每日重启。2026 年 3 月,进程在大多数工作日的午后就会触及内存上限。这一轮的快速修补没有奏效:只找到四个 bug;把内存分配器换掉没有效果;尝试优化垃圾回收也不是解法;团队不想冒险对一个已经高负载的单实例做内存剖析;重启只能买到不到一天。更麻烦的是,每日重启让服务逐渐落后得越来越远:落后超过一小时的情况出现过数次,大量任务结果没有被 listener 记录。文章澄清,这不代表那些 PR 没有跑 CI,也不代表未测试的代码进入了生产;实际情形是 listener 漏掉了部分结果,selector 于是拿过期数据决定 PR 上跑什么,主要表现为重复运行那些本来就经常失败或大面积失败的测试。
新架构:无状态 worker、journal 与独立消费进程
重写采纳了 Claude 的建议:给测试选择服务配一个数据库,准确说是内存数据存储(in-memory data store),把单实例原本在内存里承担的大量处理卸载出去。在新架构里,任何 listener worker 都可以处理任何结果,把结果追加到内存数据存储中的 journal(日志)后继续工作,不再把状态留在内存里,因而无状态、可水平扩展。另有一个独立的小型消费进程(consumer),每隔几秒把 journal 汇总成每条测试的历史记录,selector 便能快速查取相关的结果历史。
这套分布式架构的运行成本更高,但比起不稳定的单实例,扩容和内存剖析都容易得多。项目由一名工程师在三周内完成;一年前,类似的重写大约要花一个季度。后续调优(journal 的大小、worker 的数量)大部分由 Claude 自主完成,那之后服务保持稳定。博文图表标注了时间线:2026 年 5 月 9 日重写版上线,同月 14 日完成规模设定与调优,此后 listener 队列中未处理的结果事件大幅回落。
作者的建议:为指数增长提前留余量
Malhotra 说,他吃了苦头才学到的教训是:永远为指数增长做计划。如果回到 2025 年 10 月,他会做两处改变。一是把 AI 的指数增长算进容量假设:随着每位工程师对应的 agent 数量上升、PR 批准流程进一步加速,CI 任务会指数增长。二是更早注意 PR 形态的变化:Claude 偏好更小、更细的 PR,这本身就带来更多 CI 任务;agent 在夜里和周末也在推代码,把活跃度的下限抬高了,但整体节奏仍然有起伏,因为大量 PR 仍由人类工程师驱动与批准。
他给出的建议很具体:给服务配足可观测性,让它成为 Claude 的眼睛和耳朵,Claude 就能像爬山一样逐步定位并修复问题,比人工排查更快也更有效;尤其要保证进来的 CI 任务数和出去的相等;状态从一开始就要放在进程之外,关键服务不要跑成单实例,除非你能对它度量,并能灰度验证变更。
他还算了一笔时间账:这些快速修补每次争取到的时间,都只有一年前的零头;而重写整个服务所需的时间,同样只有零头,因为写代码不再是瓶颈。在他看来,「过度工程」的概念正在淡化,门槛至少已经抬高了很多:只要预算允许,v0 设计可以直接按感知规模的 10~20 倍来准备。
他给工程团队的容量建议是:无论自建还是采购,都按「两个季度内负载涨到 25 倍」来做准备。博文附带的图表显示,重写版本上线之后,CI 任务量曲线仍在攀升。
参考链接
- (Anthropic 工程博客,2026 年 9 月 14 日)