每天3万代码变更让git成瓶颈,Anthropic用AI五个月造出替代品
在 Anthropic,编码不再是构建系统或开展研究的瓶颈。Claude 能够自主迭代研究循环、微优化复杂程序,并以我们从未见过的速度和规模从零构建新服务。现在的瓶颈,是这一切为人类规模而构建的开发者基础设施:源代码管理、构建和 CI。
2026 年 5 月,Anthropic 平均每天合并约 2500 个代码变更;到 9 月,我们每天合并超过 3 万个。我们预计这一增长速度短期内不会放缓;合并代码的潜在需求几乎是无限的,每消除一个瓶颈,都会立刻带来代码产量的进一步增长。
(图:2026 年 1 月至 9 月每天合并到我们的 monorepo 的变更数。)
为了支持这种增长,我们构建了内部版本控制系统 Scion,并在五个月内将我们的 monorepo 从 git 完全迁移过来。我们还同时构建并交付了代码审查界面(名为 Antique)、代码搜索、一个功能丰富的源码控制 IAM 系统、管理并落地大量已审查代码的新颖方法等等。这篇文章将重点介绍我们如何具体构建 Scion。这是一个有趣的案例研究:Claude 的写代码能力既是我们陷入这种处境的首要原因,也是我们能够快速构建一个高质量、可扩展系统来应对它的手段。
Growing our monorepo
在探索我们构建了什么之前,我们应该承认我们的工作量是多么不寻常。不出所料,Anthropic 使用 Claude 编写软件的程度远超几乎所有其他公司,因此我们常常最先看到为人类规模构建的工具开始吃力。源代码管理只是其中之一。虽然我们预计短期内不会有太多组织遇到这些问题,但我们怀疑,其他使用 AI 编码工具的人不久后也会经历同样的压力点,这就是为什么我们想分享我们构建的东西和我们学到的经验。
Git 是一个去中心化系统:每个克隆都是完整、自包含的仓库,可以计算 diff、读取历史和 blame,并完全离线检出任何修订。对于绝大多数仓库而言,这是正确的设计,也是 git 成为标准的重要原因。
随着我们的 monorepo 增长,它越来越不适合这种设计空间。仅我们主分支的历史就有近 100 GB,压缩后的工作副本超过 25 GB。我们每天为 CI 执行约 2500 万次代码检出,这还不包括开发者环境、智能体沙箱和研究任务。如果天真地拉取,那将是每天超过 600 PB 的 git 流量。
Git 提供了处理仓库切片的工具,如浅克隆、稀疏检出和 promisor remote。除此之外,还有大型 monorepo 的标准变通方案,比如内部 git 镜像和保存在 blob 存储中的仓库快照(seeds)。我们全都用过——在高峰期,我们运行了数百个内部 git 镜像来应对负载。这为我们赢得了时间,但随着合并率的提高,它们变得越来越难以操作。Seeds 在刷新间隔内越来越偏离 main 的最新提交,而维护依赖历史的操作(如 diff 和 blame)在部分克隆上正常工作也需要越来越小心。
(图:扩展 git 检出的典型架构。)
退一步看,我们注意到,和大多数公司一样,我们把 git 当作一个集中式系统,只有一个权威副本,但我们几乎所有的客户端都是智能体和作业,它们天然在线。让 git 在分布式离线工作中表现出色的特性,随着仓库增长反而在削弱我们的扩展能力。于是我们扪心自问:如果从一开始就把智能体始终在线的事实纳入设计,源代码管理会是什么样子?
Principles for agent-scale source control
随着智能体化编码的出现,许多关于软件开发方式的常见假设正在演变。我们在构建 Scion 时拥抱了这些变化,这使我们在几个核心原则上采取了与 git 根本不同的方法。大型 monorepo 的源代码管理已有先例,尤其是 Google 的 Piper 和 Meta 的 Sapling,因此我们能够借鉴他们来之不易的经验来构建 Scion。我们的原则如下:
- 客户端始终在线。Git 假设客户端离线工作,并且需要仓库的完整副本。由于我们几乎所有的客户端都是智能体和作业,我们围绕始终可达的服务器设计了 Scion 的客户端和协议,让客户端在需要时获取数据。
- 客户端-服务器不对称。在 git 中,每个客户端都能成为完整的服务器。在 Scion 中,服务器回答关于文件历史和 blame 的问题、创建提交、执行合并和 rebase 等。客户端维护一个有界的本地对象缓存,将工作副本文件布局到磁盘上,监控文件系统变化,并给用户提供一种符合人体工程学的方式与服务器交互。它自己能计算的东西很少,但作为交换,它在本地需要的数据也少得多。
- 全面的源代码管理服务可以支撑许多新颖应用。在 git 中,一个需要询问仓库问题的 CI 作业或工具(例如谁最后修改了一行,或者两个提交之间有何关联)必须维护一份本地仓库副本,或者寄希望于特定供应商的 API 提供答案。在 Scion 中,客户端使用的所有源代码管理查询也都通过 API 暴露出来,因此在许多情况下,作业根本不需要本地仓库。如果没有这种廉价、可编程的源代码管理访问方式,我们的许多内部工具(如自动化 bisect 和 revert 流水线)在大规模下都无法实际运行。
- 一切都必须水平可扩展。在高峰期,我们运行了数百个 git 镜像,这严重消耗容量,一旦仓库超出镜像的磁盘或内存限制就无法扩展。在 Scion 中,仓库数据是分布式的,每个组件都独立可扩展;任何地方都没有完全物化的仓库副本,甚至在服务器上也没有。没有与 packfile(git 的压缩对象包)等价的东西,因此不需要仓库维护。
- 提交必须廉价、增量且无锁。在 git 中,你在本地提交,然后 push 来分享你的工作。在 Scion 中,每次提交、rebase 或合并都发生在服务器上,因此没有需要 push 的东西,但每个操作都必须快速。创建提交并更新其下游数据结构的成本必须与变更的大小成正比,而不是与仓库的整体大小成正比,否则服务器会跟不上。Scion 的开发周期不鼓励 refs(指向提交的指针,如分支和标签),除非是主 ref 或发布分支,因为每个服务器都必须达成一致的全局状态是一个迫在眉睫的扩展问题。
- 工作副本是可选的,并且设计上支持稀疏。由于我们的 monorepo 非常大,而且我们每天为 CI 检出代码数千万次,我们想尽可能避免检出。在 Scion 中,需要某个提交与其 merge base 之间 diff 的 CI 作业,例如,可以请求服务器提供。写入也是如此:许多写工作负载也可以跳过检出,完全通过 API 运行。Scion 的协议以稀疏检出(只包含客户端请求的目录)和虚拟化检出(按需物化文件)为核心,因此当作业确实需要磁盘上的文件时,客户端只为实际读取的内容买单。
How Scion works
理解这些原则在构建 Scion 时如何在实践中结合起来是很有帮助的。这些见解可以应用于其他源代码管理系统,或者更广泛地说,应用于软件开发生命周期的其他阶段。
Data structures
在 Scion 中,工作单元是“变更集(changeset)”,即预写日志中的一个条目。变更集存储了 git 中提交应具备的大部分内容。但 git 提交存储的是指向树(tree)的指针,而变更集存储的是相对于其第一个父提交发生变化的文件列表[1],以及指向 blob 存储中压缩文件内容的内容寻址指针。变更集在服务端创建,并立即可在所有地方使用。
(图:Scion 中变更集的结构。)
这种结构让我们能够以非常高的吞吐量接受变更集,因为没有 packfile 需要接受,没有锁定或跨服务器协调,也没有 refs 需要移动。Scion 在技术上确实支持 refs,但既然创建或获取提交都不涉及它们,移动 ref 就是对单个 Spanner 行的一次比较并交换(compare-and-swap),与创建变更集分开进行。
变更集只记录发生了什么变化,因此它本身无法快速回答诸如谁最后编辑了一行或哪些提交影响了一个文件之类的问题。为了回答这些问题,Scion 从变更集构建额外的数据结构,包括专用于文件 blame 和路径历史的结构,以及用于提交图的跳表。我们把所有不直接属于变更集的东西称为“派生数据”,并全部通过一种通用机制计算。每种派生数据都是递归定义的,因此它对于某个变更集的值是从该变更集父提交的值计算出来的。
这种机制有三个好处。第一,当新的数据结构能让某个操作更快时,我们可以轻松添加新的派生数据类型。第二,对于每种类型,我们可以选择是立即计算还是延后处理,从而避免物化我们不需要的数据。第三,由于客户端永远不会看到这些数据结构的内部,我们可以在不改变客户端的情况下优化它们。随着系统的发展,我们多次添加了新的派生数据类型,例如用于执行复制检测或基于路径的文件搜索的新索引。
Scaling Scion
一小群 Scion 服务器为客户端需要的任何操作提供源代码管理 API,例如获取变更集、计算 diff、查找 merge base、拉取 blob。所有源代码管理数据都是内容寻址的,因此数据一旦存储就永远不会改变,任何服务器都可以缓存它,而无需与其他服务器核对。因此,这些服务器是水平可扩展的,几乎不需要协调。它们本质上是大缓存,可以即时计算派生数据并将结果写回 Spanner。
Scion 的众多消费者不再需要维护自己的本地仓库。对于需要询问源代码管理问题(尤其是提交祖先和 blame)的 CI 作业或工具来说,获取和维护仓库曾经是一项重大的维护负担。在迁移初期,在我们拥有自己的客户端、仓库格式或协议之前,Scion 只回答这些“元数据”查询。获取文件内容和物化工作副本是数据密集得多的操作,它们需要自己的设计。
Scion 的 scn 客户端在几个重要方面偏离了 git 的 fetch 协议,因此非常适合高度可扩展的架构,所需 CPU 远少于 git 服务器。Scion 的协商不会告诉服务器客户端已经拥有什么;相反,它只针对客户端想要的 tree 和 blob。从根 tree 开始,客户端检查自己的本地存储中缺少哪些 tree,并递归请求,然后对 blob 重复该过程。每个请求都是针对一个永不变更的特定对象,因此任何缓存都可以回答,架构与传统的分层缓存非常相似。该过程也很高效且可恢复,因为没有单个数 GB 的 packfile 响应。稀疏甚至虚拟化检出不需要特殊支持,因为客户端根本不会要求它不想要的 tree。
在集群中,客户端通过 socket 与守护进程 scion-daemon 通信,后者为整个节点存储对象。我们的大多数节点需要访问 monorepo,同一节点上的作业会复用大多数相同的对象。每个 daemon 从定期刷新的快照开始,然后只与本区域的 tree 和 blob 缓存服务器(称为 scion-cache)通信。这些缓存服务器直接以 daemon 将要消费的磁盘格式从内存中流式传输对象,因此它们不需要花时间计算客户端缺失的对象或执行增量压缩。当缓存缺少某个对象时,它会请求权威的 scion-server,后者根据需要从 Spanner 或云存储读取。
scion-daemon、scion-cache 和 scion-server 都对这个协议响应相同的 RPC,因此客户端不需要知道缓存拓扑。理论上任何层级都可以相互通信,但我们严格强制执行分层以隔离故障域。我们希望避免区域 scion-cache 的故障压垮上游 scion-server 并拖垮整个系统的情况。
(图:Scion 的检出架构。)
该系统的每一层都保持超过 99% 的缓存命中率,并且不需要长期状态,因此在需要时很容易扩展。按 CPU 和内存需求计算,我们现在的 Scion 基础设施占地面积比之前的 git 镜像基础设施小 200 倍。CI 和研究作业检出代码的速度快了整整两个数量级(从分钟级变为秒级),这在整个集群中释放了大量容量;仅 CI 就实现了 25% 的 CPU 容量收益。客户端和缓存都不需要执行仓库维护,这简化了我们作业的生命周期管理。
What we learned building Scion
我们开始构建 Scion 的时候,恰逢 Claude Mythos Preview 和 Claude Tag 在公司内部可用。这两个版本同时放大了对 Scion 的需求,并显著加快了我们构建它的速度。
这里有一个特别引人注目的例子。今年早些时候,我们基本上手工编写了 git 到 Scion 摄取管道的第一个原型版本。我们以为它已经够好了——直到我们拿到 Claude Mythos Preview 的早期内部快照。我们要求它重写整个过程,在没有任何额外指示的情况下,它生成了一个内存消耗降低 20 倍、速度快 4 倍的版本,同时保留了所有关于摄取顺序和数据结构的隐式不变量。
(图:2026 年 1 月至 9 月每周合并到 Scion 和 Antique 代码库的变更数。)
我们在构建和运营 Scion 中学到的东西,为构建智能体规模软件可能的样子提供了一些有趣的启示。
Rapid development with a small team will become more common.
一个七人团队构建了 Scion 和 Antique(我们的代码审查工具,仅它就足以写一整篇博客文章),更不用说还有六个相关系统,并在五个月内完全迁移到这些系统上。我们完成这一切的同时,每天合并的代码量增长了 10 倍。过去,开发和部署这样一个源代码管理系统,需要一家公司多年的迭代迁移和更多工程师。我们认为这种快速、灵活的迁移方式未来会变得更加普遍。
Engineers are no less valuable, but their work is changing.
参与该系统的所有工程师现在都会认为 Claude 写代码比他们更好。但我们的工程师仍然提供了宝贵的、对高层级系统间组件和设计层面权衡的理解,围绕我们想要解决的问题和我们知道需要达到的规模做出明智的选择。我们经常发现,工程师的角色是对 Claude 说“不”。智能体有时会违反核心系统约束,以满足一个本就不应该被允许的模式。这在我们的迁移过程中尤其常见。例如,有多个系统试图通过线性读取提交日志来暴力查找一个变更何时合并到 main,而不是询问我们的代码审查工具。Anthropic 也广泛使用对抗性审查,虽然这在很多方面极其有效,但它也可能是短视的,因此熟练的工程师需要引导 Claude 远离为了回应假设性问题而过度设计系统部分的行为。例如,有一次审查对信任另一个内部服务表现得过于防御性,导致 Claude 添加了过度的验证和复杂性。我们的团队成员仍然会审查系统的每一个变更请求,但他们现在检查的是变更的意图,而不是逐行阅读代码。
We were able to substantially improve optimization and quality.
由于 Claude 能够以比我们自己快得多的速度编写海量代码,Scion 是我们所从事过的测试最完善、防御最严密的代码库之一。即使在紧张的时间表内,我们也能够对我们遇到过的所有非平凡基础设施和源代码管理交互构建封闭模拟,以及进行模糊测试和精细的速率限制。Claude 还轻松发现了重要的优化——例如,对祖先查询的一种算法优化,绕过了我们某个索引——这些优化本需要专家数天或数周的分析和调优……然后第二天它又做到了。
(图:在原生 Scion 仓库上观察到的性能改进。)
We laid the groundwork for new forms of monorepo security.
在大型 monorepo 中管理安全性、安全性和合规性与管理其规模和体量同样重要。我们从一开始就考虑到了这些来构建 Scion。它支持对仓库的丰富访问控制,以及将权限应用于单个目录的能力,从而在保护最敏感代码的同时不失去 monorepo 的好处。此外,我们构建的代码审查工具可以在批准上表达复杂的语义约束,这使我们能够区分简单的事情(如仅注释变更)和敏感的逻辑变更,从而让人类审查者专注于最重要的变更。
A new level of operational excellence is possible.
我们的团队大量使用 Claude Tag,这帮助我们监控系统的每个组件。例如,我们创建了 Slack 频道,在这些频道里,我们让 Claude 全神贯注地关注单个组件的健康状况。我们称这些 Claude 为 “superfans”。我们有一个 scion-daemon superfan、一个 scion-cache superfan、一个 Antique 合并队列 superfan,等等。它们可以访问我们的遥测数据和日志,就像一位经验丰富的工程师,除了刷新图表和寻找任何异常之外没有其他事可做。我们也为所有系统设置了传统告警,但我们的 superfans 能够检测到微妙且新颖的问题,相比之下,这些传统告警显得非常单一。
We’re completely rethinking our priors.
这个项目促使我们非常认真地审视其他为人类规模设计的其他工作流程。例如,一个严格的合并队列在智能体规模下意义不大。当数百甚至数千个变更可以在测试运行期间合并时,CI 到底意味着什么?我们无法有效地复制行业标准的全局仓库锁合并队列,因此,我们转向投资于合并后和部署前的自动化测试。这确实意味着并发变更可能会冲突,但 Claude 通常能比人类发现冲突的速度更快地修复这些冲突。
Contributing to the wider ecosystem
我们将 Scion 视为应对日益强大的 AI 智能体所带来的扩展问题的一个答案组成部分。我们分享这些细节,是作为团队如何应对类似挑战的一个例子,尽管我们并不期望我们的解决方案或考虑适合所有情况。
Scion 绝不是 git 在通用场景下的合适替代品。我们在这里概述的概念只对真正庞大的 monorepo 才有意义,要么有数万名工程师,要么有大量智能体大量编写代码。此外,Anthropic 的独特之处在于,我们用于内部使用的 token 消耗能力远超任何其他企业现实愿意花费的水平。我们这样做既是为了满足我们自己的软件开发需求,也是为了测试 Claude 的能力。
从实践上讲,很难提供一种对其他组织广泛有用的 Scion 形式。它与 git 有很大差异,意味着公司需要迁移开发生命周期的每一个环节,而且它需要自己的代码审查、代码搜索和 CI。我们这些组件的内部版本高度定制,因此其他组织很难采用,而且我们担心,试图构建一个试图取悦所有人的公开版本实际上并不会非常有用。
然而,我们预计这类问题在未来 6 到 12 个月内会变得更加普遍,如果每个使用 git 的公司都独自面对类似的挑战,那将是工程资源的低效使用。鉴于此,我们认为我们能做的最有影响力的事情就是分享我们的思考,这样其他开发者就可以在不偏离整体模型的情况下,将 Scion 最成功的部分应用到 git 上。git 服务器不必与 git 客户端以相同方式工作;存在能够显著改善扩展性的新型服务器架构。Git 平台可以提供像 Scion 这样的全面服务器端 RPC,也许甚至可以在各个 forge 之间以一种标准化的方式提供,让 git 客户端能够学会与之对话。协商协议也不必像今天这样,git 中的各种稀疏和浅层功能可以很好地组合在一起。
最后,我们想感谢 git 社区花了近二十年时间,为数百万开发者扩展这个平台。我们的贡献要窄得多:分享我们关于智能体化软件开发如何与这些系统交互的早期视角,以便我们能与 GitHub、GitLab 和开源社区合作,支持开发者不断变化的需求。在软件开发生命周期与 AI 指数级发展交汇之际,我们都在同一条船上。
Acknowledgements
作者:Nick G. 与源代码管理团队:S.G., R.K., Dan C., Garrett H., Ben L., Colin M., Joe P. 我们感谢 Vilas J. 和 Sergiu D. 在这个雄心勃勃的旅程中对源代码管理团队的支持,以及 Amir O. 对 Antique 的早期贡献。
Footnotes
[1] 存储相对于合并第一个父提交的改变文件是一种风格选择;在实践中,我们发现人们只关心 monorepo 中基于主干开发的主线上提交的历史或 blame。