瓶颈换了位置
当写代码不再是最慢的一步,压力会往哪里走?Anthropic 的 CI 基础设施工程师 Sachin Malhotra 在 9 月 14 日的工程博客里给出了一个具体的答案:走到测试与评审这一环。
文章里的几个数字放在一起看,含义很清楚。CI 任务量在半年里涨了 25 倍,代码库里的测试总数涨了 10 倍,而工程师人数只是「名义上」增加。同一时间段里,平均每位工程师每季度交付的代码量是 2021 到 2025 年那几年的 8 倍,其中约 80% 由 Claude 撰写;它也在评审与批准 PR 上承担了相当一部分工作。
这些是公司自报的运营指标,不是经过独立审计的生产力研究,代码量增长也不等于功能、收入或软件质量同比例增长。但作为一份内部失效复盘,它描述的因果链条是完整的:代码变多,测试变多,而测试选择服务的容量没跟上。
那个服务在做什么
Anthropic 并没有在每个变更上跑全部测试。它的做法是维护一个确定性的测试影响分析服务,根据历史表现与包的相关性判断每个变更该跑哪些测试。这个服务由两个组件组成:一个 listener 记录每次 CI 运行的测试结果,一个 selector 读这些历史,决定一个新开的 PR 要跑哪些测试。
设计本身不罕见,市面上也有专门做这件事的产品。问题出在实现上:这套服务最初跑在单个进程里,因为维护「每个测试的运行历史」需要一个单一写者。这个约束直接堵死了水平分片的可能。
一旦每秒都有多个 CI 任务在跑,listener 就开始落后于 PR 队列。落后的代价在 AI 原生的开发节奏里被放大了:20 分钟的 listener 滞后,意味着数万条测试更新没有应用到 selector 上。接下来是一串连锁反应——坏改动合并后测试对所有人开始失败,不稳定用例开始阻塞合并,刚修好或刚新增的测试不能及时跑起来。
原文特意澄清了一点:这不等于跳过了 CI 或未测试的代码进了生产。它带来的是一连串额外的排查成本。
三次补丁的衰减曲线
到 2025 年 10 月,这个服务已经显示出压力,团队连续两天被报警叫醒。接下来是三次修补。
补丁一是加倍核数,所有人都知道这是权宜之计,它撑了 70 天。补丁二是按包分片——团队意识到 listener 并不需要全局单一写者,而是每个包一个写者,于是把每个包的状态拆到独立的 shard 与 worker 上。这一段分片代码由 Claude 生成,它撑了 29 天。补丁三是每日重启:到 3 月,进程在工作日的午后就会撞到内存上限,团队只找到四个 bug,换了内存分配器也没有改善,重启能买到的时间不到一天。
70 天、29 天、不到一天。每一次修修补补买到的时间比上一次少一个数量级,这是指数增长撞上线性修复时的典型样子。作者后来总结的教训就是这一条:按指数去规划。
这三次补丁的衰减,也说明了「先扛住再重构」这个策略在什么时候会失效。它在增长斜率平缓时很划算,在斜率陡峭时会连续失败三次,而且失败的方式一次比一次短命。
真正的动作是把状态搬出进程
最终方案不是更聪明的补丁,而是拆掉那个约束本身。
改造后,listener 被赋予一个内存数据存储,任何 worker 都能处理任何结果,把它追加到日志里然后继续,不需要在进程内保留状态。一个小的消费者进程每隔几秒把日志汇总成逐测试的历史,selector 按需查询这份汇总视图,而不是直接读实时流。
这个结构听起来像事件溯源加异步物化视图,也确实不新。值得记下的是它的代价曲线:新架构的运营成本高于原来的单进程,但更容易扩展,也更容易做内存分析,因为不再有一个有状态单体需要在生产环境里整体压测。而完成这次改造用了一位工程师三周时间,作者估计在一年前的开发节奏下,这接近一个季度的工作量。
还有一个细节很有意思:整个排障过程中,作者一直挂着一个长期运行的内部 Claude 会话专门盯这个服务,一旦 listener 落后超过 5 万个任务就通知他。按他的说法,Claude 多次主张彻底重写,而人通常选择再打一个补丁。最后采纳的正是重写这个建议。
一条最容易被忽略的检查
在这篇文章的分析里,有一条提醒比架构本身更适合被搬走。
这套测试选择系统在「变陈旧」时,倾向于跑更多的测试——浪费算力,但不会漏。作者把它称为一个幸运的默认值,而不是设计上的保证。
反过来想就很清楚:如果这个系统的退化方向是少跑测试,那么过去半年里增长 25 倍的负载,呈现出来的不会是报警与排队,而是一段静默的正确性回归——没有测试失败,因为该跑的测试根本没错。
所以真正该做的检查有两层。一层是确认你的门禁服务在落后时朝哪个方向退化;另一层是确认这个方向被显式设计过,而不是碰巧。
对普通团队的适用边界
这套经验不能机械照搬,原因在于量级。
Anthropic 的 25 倍增长来自一个特定组合:代码量涨 8 倍、测试涨 10 倍、人数几乎不变。多数团队的曲线要平缓得多,用不着为了未来的可能负载提前重构一个有状态服务。对它们更有价值的是另外三件事。
其一,把「谁是写者」这个问题在架构评审时问一遍。只要存在一个必须单一的写者,水平扩展就被判了死刑——这个约束往往不是因为业务需要,而是因为实现方式。
第二,把「记录发生过什么」与「决定什么重要」分开。这两件事对吞吐的要求完全不同:前者是写入密集且必须顺序执行,后者是查询密集且可以延迟。放在同一个进程里,它们只能一起变慢。
第三,给容量规划一个显式的倍数假设。不用照抄 25 倍,但要写下来一个数字,并说清它在什么条件下成立。没有这个数字,容量讨论最后只能落到「先加机器看看」。
至于 CI 会不会成为更多团队的瓶颈,作者的判断是:随着智能体同时抬高 PR 数量与测试数量,水平扩展的测试选择架构会变成行业标配。这个判断带有明显的自身经验色彩,但它指出的方向——代码生成提速之后,压力会顺流而下——对任何在规模化使用编码智能体的团队都成立。
目前官方及行业暂未披露更多细节,后续将持续跟进迭代动态。