Anthropic 工程复盘:AI 编码激增下 CI 测试影响分析架构演进
Agentic coding is straining CI. Here’s how we scaled test impact analysis at Anthropic
Agent 编码普及后 CI 瓶颈是行业共性难题,这篇一手工程复盘给出了完整的架构演进路径和取舍权衡,值得相关团队参考。
Agentic coding is straining CI. Here’s how we scaled test impact analysis at Anthropic
Agent 编程正在给 CI 带来压力。以下是我们在 Anthropic 扩展测试影响分析的方法
Our CI job volume increased 25x over 6 months. We patched our test selection service three times before finding a sustainable solution.
我们的 CI 作业量在 6 个月内增加了 25 倍。在找到可持续的解决方案之前,我们对测试选择服务进行了三次修补。
- Category
- Claude Code
- Enterprise AI
- Product
- Claude Code
- Claude Enterprise
- Claude Tag
- Date
- September 14, 2026
- Reading time
- 5
- min
- Share
- Copy link
- https://claude.com/blog/agentic-coding-is-straining-ci-heres-how-we-scaled-test-impact-analysis-at-anthropic
- Author(s)
- Sachin Malhotra
- 类别
- Claude Code
- 企业 AI
- 产品
- Claude Code
- Claude Enterprise
- Claude Tag
- 日期
- 2026 年 9 月 14 日
- 阅读时间
- 5
- 分钟
- 分享
- 复制链接
- https://claude.com/blog/agentic-coding-is-straining-ci-heres-how-we-scaled-test-impact-analysis-at-anthropic
- 作者
- Sachin Malhotra
AI is evolving CI
AI 正在重塑 CI
Anthropic engineers on average ship 8x as much code per quarter as they did from 2021-2025. Claude authors 80% of that code and it also plays a large role in reviewing and approving PRs as well.
Anthropic 工程师每季度提交的代码量是 2021-2025 年期间的平均水平的 8 倍。Claude 生成了其中 80% 的代码,并在审查和批准 PR(Pull Request)方面也发挥着重要作用。
Writing code is no longer the constraint, and once PR review gets accelerated, CI starts feeling the pressure.
编写代码不再是瓶颈,一旦 PR 审查速度加快,CI 就开始感受到压力。
On top of that, the amount of tests across our codebase grew 10x and we added a nominal amount of engineers. This all led to a 25x increase in CI jobs over a six month period (in case you are trying to do the math, not every test runs on every PR as I will explain).
此外,我们代码库中的测试数量增长了 10 倍,而工程师人数仅小幅增加。这导致 CI 作业量在六个月内增加了 25 倍(如果你正在尝试计算,请注意并非每个测试都会在每次 PR 中运行,我稍后会解释)。
This threatened to overload our test impact analysis service several times. To avoid becoming the next bottleneck, we blew up the whole thing and reimagined what the service's architecture looks like. But getting there was a bumpy path that started with three quick fixes, which lasted 70 days, then 29 days, and then less than a day respectively.
这曾几次威胁到要压垮我们的测试影响分析服务。为了避免成为下一个瓶颈,我们彻底推翻了原有架构并重新设计了服务的结构。但到达这一步的过程充满坎坷,始于三次快速修复,分别持续了 70 天、29 天和不到一天。
Scaling CI is a challenge more engineering teams are likely to soon face as agents continue to accelerate code generation and review. I anticipate horizontally scaled test selection architecture will become industry standard as teams running agents create both more PRs and more tests.
随着 Agent 继续加速代码生成和审查,更多工程团队很可能很快面临扩展 CI 的挑战。我预计,随着使用 Agent 的团队产生更多的 PR 和更多的测试,水平扩展的测试选择架构将成为行业标准。
In this article, I’ll discuss how we scaled our test impact analysis service at Anthropic and the lesson I learned the hard way: always plan for the exponential. The specific scaling techniques–buying bigger machines, parallelizing processes, or restarting the service (yeah, this one still works surprisingly well) – are common and not the insights to take from this article.
在本文中,我将讨论我们如何在 Anthropic 扩展测试影响分析服务,以及我通过惨痛教训学到的经验:务必为指数级增长做好准备。具体的扩展技术——购买更大的机器、并行化处理流程或重启服务(是的,这一招仍然出奇地有效)——都是常见手段,并非本文要传达的核心见解。
The point is that each of these techniques bought a fraction of the time they did a year ago. On the other hand, overhauling and completely redesigning a service also takes a fraction of the time and is much more sustainable now that writing code is no longer the bottleneck.
关键在于,这些技术所节省的时间仅为一年前所需时间的几分之一。另一方面,彻底 overhaul 并完全重新设计一个服务现在也只需花费很少的时间,而且随着编写代码不再是瓶颈,这种做法变得更加可持续。
The more you can anticipate this strain and plan how your architecture will evolve with it, the less time you will waste on half-measures.
你越能预见这种压力并规划架构如何随之演进,你就越少会在半途而废的措施上浪费时间。
The test impact analysis architecture
测试影响分析的架构
Many of my peers work at organizations where every test is still run on every change. This works up to a point, but doesn’t scale: CI gates get increasingly long, expensive, and untrustworthy.
我的许多同事所在的组织仍在每次代码变更时运行所有测试。这在一定程度上可行,但无法扩展:CI 门禁时间越来越长,成本越来越高,且可信度下降。
Additionally, humans are great at determining which test failures don’t apply to them while agents will require more context and direction. When they get a specific set of valid tests, they can self-verify and iterate more effectively.
此外,人类非常擅长判断哪些测试失败与己无关,而 AI 代理则需要更多的上下文和指引。当它们获得一组有效的测试时,可以更有效地进行自我验证和迭代。
At Anthropic, we built a deterministic test impact analysis or test selection service that determines which tests run on each change based on past performance and package relevance. This isn’t an uncommon practice, and there is a category of vendors with offerings in this area.
在 Anthropic,我们构建了一个确定性的测试影响分析或测试选择服务,该服务根据过往表现和包的相关性来决定每次变更运行哪些测试。这并不是不常见的做法,市场上有一类供应商提供此类解决方案。
Our service depends on two deterministic components staying in sync:
我们的服务依赖于两个确定性组件保持同步:
- A “listener” records the test results from every CI run.
- A “selector” reads the test result history and determines which tests run on which opened PRs.
- 一个“监听器”记录每次 CI 运行的测试结果。
- 一个“选择器”读取测试结果历史,并决定哪些测试应针对哪些已打开的 PR 运行。
This is effective, but when there are multiple CI jobs running every second, the listener starts to increasingly fall behind the PR queue. For an AI-native SDLC, a small lag can have a big impact. For example, 20 minutes of listener lag can translate into tens of thousands of test updates not being applied to the selector.
这种方法虽然有效,但当每秒有多个 CI 作业运行时,监听器会逐渐落后于 PR 队列。对于原生 AI 的软件开发生命周期(SDLC),微小的延迟也会产生巨大影响。例如,监听器滞后 20 分钟可能导致数万条测试更新未能应用到选择器中。
- If a bad change gets merged, then a test will start failing for everyone else causing multiple unnecessary investigations.
- If a dependency starts flaking, then flaky reds start blocking merges.
- If a test gets fixed or a new one gets added, it won't run until the listener catches up risking a regression.
- 如果错误的变更被合并,其他所有人的测试都会开始失败,导致多次不必要的排查工作。
- 如果某个依赖项开始出现不稳定现象,不稳定的红色状态就会开始阻塞合并。
- 如果某个测试被修复或新增了一个测试,在监听器赶上之前它不会运行,从而存在回归风险。
All of this ran as a single process because keeping a running history per test meant a single writer needed to apply the results. This v0 design prevented us from being able to horizontally shard.
所有这些都在单个进程中运行,因为为每个测试维护运行历史意味着需要单一写入者来应用结果。这种 v0 设计使我们无法进行水平分片。
The bumpy road to redesign
重新设计的坎坷之路
By October of last year the service was already showing signs of strain, and we got paged two days straight.
到去年十月,该服务已经显示出压力迹象,我们连续两天收到告警通知。
Patch 1: A bigger machine
补丁 1:更大的机器
The first fix was easy: we doubled the cores running the service. We also knew it would be fleeting.
第一个修复很简单:我们将运行服务的核心数翻倍。我们也知道这将是暂时的。
Conversation recreated. Based on real events.
对话重现。基于真实事件。
Even when the trend line was clear, ownership was murky. No one wanted to own another piece of infrastructure. Also, the CI team had bigger fish to fry.
即使趋势线很清晰,所有权也很模糊。没有人愿意接手另一部分基础设施。此外,CI 团队有更重要的事情要做。
Patch 2: Sharding
补丁 2:分片
At this point we were getting paged pretty frequently by the lag building up in the listener of this service. To drive some long-term fixes, I started a long-running session in an internal version of Claude Tag dedicated to monitoring the service. Anytime the listener lag would get more than 50,000 jobs behind, Claude would ping me and resume our conversation on next steps.
此时,由于该服务监听器中积压的延迟,我们经常收到告警通知。为了推动一些长期修复措施,我在一个内部版本的 Claude Tag(专门用于监控该服务)中启动了一个长时间运行的会话。每当监听器积压超过 50,000 个作业时,Claude 就会通知我并恢复关于下一步措施的对话。
This would go on for months, and it was helpful not having to constantly remind it of past efforts or context. Claude often argued for an overhaul, but we usually settled on another patch.
这种情况持续了几个月,而且无需不断提醒它过去的努力或上下文,这很有帮助。Claude 经常主张彻底 overhaul(重构),但我们通常选择另一个补丁。
Verbatim conversation on an internal version of Claude Tag with some redactions.
与内部版本 Claude Tag 的逐字对话,部分内容已删减。
In February, the exponential growth of CI jobs started to strain the service once again. This time, we decided to parallelize.
二月,CI 作业的指数级增长再次给服务带来压力。这次,我们决定并行化。
The listener didn’t need a single writer to order test results correctly, it needed a single writer per package to order the test results for each section of our codebase correctly. Claude generated the code for us to split each package’s state into a shard with its own worker.
监听器不需要单一的写入者来正确排序测试结果,它只需要每个包有一个单一的写入者来正确排序代码库各部分的测试结果。Claude 为我们生成了代码,将每个包的状态拆分为带有自己工作进程的 shard(分片)。
We also knew this fix would be fleeting, but we didn’t realize it would only buy us 29 days.
我们也知道这个修复是暂时的,但我们没意识到它只能为我们争取到 29 天。
Patch 3: Daily restarts
补丁 3:每日重启
In March, the process reached its memory limit by mid-afternoon on most weekdays. Again, we looked for quick fixes but:
三月,在大多数工作日,该进程通常在下午中期达到内存限制。同样,我们寻找快速修复方案,但是:
- We only found four bugs.
- Swapping the memory allocator as a quick-hack did nothing. We were trying to optimize garbage collection but that wasn’t really the solution.
- We didn’t want to risk memory profiling a singleton already under a heavy load.
- Restarting bought us less than a day.
- 我们只找到了四个 bug。
- 作为快速黑客手段交换内存分配器毫无作用。我们试图优化垃圾回收,但这并不是真正的解决方案。
- 我们不想冒险对一个已经承受重负载的单例进行内存分析。
- 重启为我们争取的时间不到一天。
We also discovered daily restarts were resulting in the service gradually falling further behind. When it fell behind for more than an hour, which happened several times, a ton of job results weren’t recorded by the listener.
我们还发现每日重启导致服务逐渐落后更多。当落后超过一小时时(这种情况发生过几次),大量作业结果未被监听器记录。
To be clear, this doesn’t mean CI never ran on those PRs, or that untested code was pushed to production. What it meant was that the listener didn’t pick up some results, which meant our test-selection component was using stale data to decide what to run and what not to on PRs. Mostly this translated into us running tests that were already super flaky or widespread-failing across the board.
明确一点,这并不意味着 CI 从未在这些 PR 上运行过,也不意味着未经测试的代码被推送到了生产环境。实际情况是,监听器没有捕获到部分结果,这意味着我们的测试选择组件使用了过时数据来决定在 PR 上运行哪些测试、不运行哪些测试。这主要导致我们运行了那些已经非常不稳定或全面广泛失败的测试。
The redesign
重新设计
It was (past) time to redesign the service, and we took Claude’s advice: we gave the test selection service a database, or an in-memory data store to be exact. By doing so, we effectively offloaded a huge chunk of in-memory processing that the singleton used to do.
是时候(过去时)重新设计该服务了,我们采纳了 Claude 的建议:我们为测试选择服务添加了一个数据库,或者更准确地说,是一个内存数据存储。通过这样做,我们有效地将单例模式曾经执行的大量内存处理工作卸载了出去。
Now, any listener worker can process any result, append it to a journal in the in-memory store, and move on without holding anything in memory - stateless and hence, horizontally scalable. A small separate consumer process rolls the journal up into per-test history every few seconds, and the selector can look up relevant result history quickly.
现在,任何监听器工作进程都可以处理任何结果,将其追加到内存存储中的日志里,然后继续执行而无需在内存中保留任何内容——无状态,因此可以水平扩展。一个小型的独立消费者进程每隔几秒将日志汇总为每个测试的历史记录,选择器可以快速查询相关的结果历史。
This distributed architecture is more expensive to run, but it is much easier to scale and memory profile than a shaky singleton.This project took three weeks for a single engineer. A year ago it would have been closer to a quarter.
这种分布式架构的运行成本更高,但相比脆弱的单例模式,它更容易进行扩展和内存分析。这个项目一名工程师花了三周时间完成。一年前,这可能需要接近一个季度的时间。
更进一步:量化金融体系
看懂新闻只是起点——沿量化金融路径,把它变成能交付的工程能力