Anthropic 介绍自研版本控制系统 Scion 的设计与迁移
Redesigning source control at Anthropic
Anthropic 用五个月将其 monorepo 从 git 完整迁移至自研版本控制系统 Scion,以应对代码合并量快速增长带来的基础设施瓶颈。Scion 面向始终在线的 agent 和任务采用客户端—服务器架构,支持稀疏及虚拟化检出,并通过 API 提供多类仓库查询。
在 Anthropic,编写代码已不再是构建系统或开展研究的瓶颈。Claude 能够自主迭代研究流程、对复杂程序进行微优化,并以我们前所未见的速度和规模从零开始构建新服务。如今,瓶颈变成了所有为人类工作规模而构建的开发者基础设施:源代码管理、构建和 CI。
2026 年 5 月,Anthropic 平均每天合并约 2,500 项代码变更;截至 9 月,我们每天合并的变更已超过 30,000 项。我们预计,这一增长速度短期内不会放缓;合并代码的潜在需求几乎是无限的,每消除一个瓶颈,我们产出的代码量就会立即进一步增加。

2026 年 1 月至 9 月,我们的单体代码仓库每天合并的变更数量。
为了支撑这一增长,我们构建了内部版本控制系统 Scion,并在五个月内将整个单体代码仓库从 git 迁移了过去。在同一时期,我们还构建并交付了代码审查界面(名为 Antique)、代码搜索功能、完善的源代码管理 IAM 系统,以及管理和合入大量已审查代码的新方法等。在本文中,我们将重点介绍 Scion 的构建过程。这是一个很有意思的案例:Claude 编写代码的能力,既是我们最初陷入这种局面的原因,也是 我们能够迅速构建高质量、可扩展系统来应对这一局面的关键。
不断壮大的单体代码仓库
在介绍我们构建的系统之前,需要先说明我们的工作负载有多么不同寻常。不出所料,Anthropic 使用 Claude 编写软件的程度远超几乎所有其他公司,因此,我们往往最先发现那些为人类工作规模而构建的工具开始不堪重负。源代码管理只是其中一个例子。虽然我们预计大多数组织在短期内还不会遇到这些问题,但我们认为,用不了多久,其他使用 AI 编程工具的组织也会遇到同样的瓶颈。这就是我们希望分享所构建的系统及经验的原因。
Git 是一个去中心化系统:每个克隆都是一个完整、自包含的仓库,可以在完全离线的情况下计算差异、读取历史记录和逐行追溯信息,并检出任意修订版本。对于绝大多数仓库而言,这是正确的设计,也是 git 成为标准的重要原因。
随着我们的单体代码仓库不断增长,它越来越不适合这种设计。仅主分支的历史记录就接近 100 GB,而一份压缩后的 工作副本也超过 25 GB。我们每天仅为 CI 执行的代码检出操作就达到约 2,500 万次,还不包括开发者环境、智能体沙箱和研究任务。如果直接拉取这些数据,每天产生的 git 流量将超过 600 PB。
Git 提供了针对仓库局部进行操作的工具,例如浅克隆、稀疏检出和承诺远程仓库(promisor remotes)。除此之外,针对大型单体代码仓库还有一些常见的变通方案,例如内部 git 镜像,以及保存在 blob 存储中的仓库快照(种子)。这些方法我们都用过——在高峰期,我们运行了数百个内部 git 镜像来应对负载。这些措施为我们争取了时间,但随着合并速率上升,运维难度也越来越大。种子在两次刷新之间会越来越落后于主分支的最新状态,而要让 diff 和 blame 等依赖历史记录的操作在部分克隆上正常工作,也需要投入越来越多的精力。

扩展 git 检出能力的典型架构。
退一步来看,我们注意到,和大多数公司一样,我们把 git 当作集中式系统来使用,只有一个权威副本;但我们的客户几乎全都是智能体和作业,而它们天生就始终在线。随着代码仓库不断增长,git 的特性原本让它非常适合分布式离线工作,却也在积极阻碍我们扩展规模。于是我们问自己:如果从一开始就考虑到智能体始终在线,以此为前提设计源代码管理系统,它会是什么样子?
面向智能体规模的源代码管理原则
随着智能体编程的发展,软件开发中的许多常见假设正在发生变化。构建 Scion 时,我们拥抱了这些变化,因此在若干核心原则上采取了与 git 截然不同的方法。超大型单体代码仓库的源代码管理已有先例,尤其是 Google 的 Piper 和 Meta 的 Sapling,2 因此,我们在构建 Scion 时得以借鉴它们来之不易的经验。我们的原则是:
客户端始终在线。Git 假设客户端会离线工作,因此需要拥有完整的代码仓库副本。由于我们的客户端几乎全都是智能体和作业,我们围绕始终可访问的服务器设计了 Scion 的客户端和协议,并让客户端在需要时获取数据。
客户端与服务器并不对称。在 git 中,每个客户端都能充当功能完备的服务器。在 Scion 中,服务器负责回答有关文件历史和代码归属的问题、创建提交、执行合并和变基等操作。客户端维护有界的本地对象缓存,在磁盘上组织工作副本文件,监控文件系统变化,并为用户提供符合人体工学的方式来与服务器协作。客户端本身能完成的计算很少,但作为交换,它需要在本地保存的数据也少得多。
全面的源代码管理服务可以支持许多新颖的应用。 使用 git 时,CI 作业或工具如果需要查询代码仓库相关信息,例如某一行最后由谁修改,或两个提交之间有何关联,就必须维护一份本地仓库副本,或者寄望于供应商专属 API 能提供答案。使用 Scion 时,客户端使用的所有源代码管理查询也都通过 API 暴露出来,因此在许多情况下,作业完全不需要本地代码仓库。许多内部工具,例如我们的自动化二分查找和回退流水线,如果没有这种低成本的编程式源代码管理访问方式,就无法在规模化场景下切实可行。
所有组件都必须支持水平扩展。 在峰值时期,我们运行了数百个 git 镜像,这大大增加了容量压力;而当代码仓库超过镜像的磁盘或内存限制后,这种方式也不再能扩展。在 Scion 中,代码仓库的数据是分布式的,每个组件都可以独立扩展;任何地方都不存在完整物化的代码仓库副本,即使在服务器上也没有。这里没有等同于 packfiles(git 对象的压缩包)的机制,因此也不需要维护代码仓库。
提交必须开销低、增量式且无锁。 在 git 中,你先在本地提交,然后推送以分享你的工作。在 Scion 中,每次提交、变基或合并都在服务器上进行,因此无需推送,但每项操作都必须足够快。创建提交及更新其下游数据结构的开销必须随变更规模而非整个仓库的规模增长,否则服务器就无法跟上。Scion 的开发生命周期不鼓励使用 refs(指向提交的指针,例如分支和标签),主引用或发布分支除外,因为每台服务器都必须达成一致的全局状态是一个潜在的扩展性问题。
工作副本是可选的,而且在设计上就是稀疏的。 由于我们的单体仓库非常庞大,而且每天要为 CI 检出代码数千万次,我们希望尽可能避免检出。例如,使用 Scion 时,需要获取某次提交与其合并基点之间差异的 CI 作业可以直接向服务器请求。写入也是如此:许多写入工作负载也可以跳过检出,完全通过 API 运行。Scion 的协议以稀疏检出和虚拟化检出为核心:前者只包含客户端请求的目录,后者按需将文件实体化。因此,当作业确实需要磁盘上的文件时,客户端只需为实际读取的内容付出开销。
Scion 的工作原理
了解这些原则在构建 Scion 的实践中如何结合起来,会很有帮助。这些见解可以应用于其他源代码控制系统,或者更广泛地应用于软件开发生命周期的其他阶段。
数据结构
在 Scion 中,工作单元是“变更集”,即预写日志中的一条记录。变更集存储了你通常期望在 git 提交中看到的大部分信息。不过,git 提交存储的是指向树的指针,而变更集存储的是相对于其第一个父提交发生变更的文件列表,1以及指向 blob 存储中压缩文件内容的内容寻址指针。变更集在服务器端创建,并立即在所有位置可用。

Scion 中变更集的结构。
这种结构让我们能够以极高的吞吐量接收变更集,因为无需接收打包文件,无需加锁或跨服务器协调,也无需移动引用。从技术上说,Scion 确实支持引用,但由于创建和获取提交都不涉及引用,移动引用只需对 Spanner 中的单行执行一次比较并交换操作,该操作与创建变更集分开进行。
变更集只记录发生了什么变更,因此仅靠它本身无法快速回答诸如谁最后编辑了某一行、哪些提交影响了某个文件等问题。为了回答这些问题,Scion 会根据变更集构建额外的数据结构,包括专门用于文件逐行归责和路径历史的数据结构,以及用于提交图的跳表。我们将所有不直接属于变更集的数据称为“派生数据”,并通过一种通用机制计算所有这些数据。每种派生数据都采用递归定义,因此某个变更集的派生数据值是根据其各个父变更集的对应值计算出来的。
这种机制有三个好处。首先,当某种新的数据结构能加快某项操作时,我们可以轻松添加一种新的派生数据类型。其次,对于每种类型,我们可以选择立即计算,也可以推迟计算,从而避免将不需要的数据物化。第三,由于客户端永远看不到这些数据结构的内部实现,我们可以在不更改客户端的情况下对其进行优化。随着系统的发展,我们已经多次添加新的派生数据类型,例如用于执行复制检测或基于路径的文件搜索的新索引。
Scion 的扩展
一组规模不大的 Scion 服务器提供源代码管理 API,支持客户端所需的各种操作,例如获取变更集、计算差异、查找合并基点以及获取 blob。所有源代码管理数据都采用内容寻址,因此一份数据一旦存储就不会再改变,任何服务器都可以缓存它,而无需与其他服务器核对。因此,这些服务器可以水平扩展,且几乎不需要协调。它们本质上是大型缓存,同时也能即时计算派生数据,并将结果写回 Spanner。
Scion 的众多使用方不再需要维护各自的本地仓库。过去,对于需要查询源代码管理信息的 CI 作业或工具而言,获取和维护仓库是一项繁重的维护工作,尤其是在查询提交祖先关系和逐行追溯信息时。在迁移初期,我们还没有自己的客户端、仓库格式或协议,Scion 只负责回答这些“元数据”查询。获取文件内容和物化工作副本涉及的数据量要大得多,因此需要专门设计。
Scion 的 scn 客户端在几个重要方面不同于 git 的获取协议,因此适合采用高度可扩展的架构,所需的 CPU 资源也显著少于 git 服务器。Scion 的协商过程不会告知服务器客户端已经拥有哪些对象,而是只围绕客户端需要的树和 blob 展开。客户端从根树开始,检查本地存储中缺少哪些树,并递归请求这些树,然后对 blob 重复这一过程。每个请求都针对一个永远不会改变的特定对象,因此任何缓存都可以响应,整个架构与传统的分层缓存非常相似。由于不会产生单个数 GB 大小的 packfile 响应,这一过程也很高效,并且可以断点续传。稀疏检出甚至虚拟化检出都不需要特殊支持,因为客户端根本不会请求它不需要的树。
在集群中,客户端通过套接字与守护进程 scion-daemon 通信,该守护进程为整个节点存储对象。我们的大多数节点都需要访问单体仓库,同一节点上的作业会复用大部分相同的对象。每个守护进程都以定期刷新的快照为起点,之后只与所在区域内名为 scion-cache 的树和 blob 缓存服务器通信。这些缓存服务器直接从内存中以守护进程将使用的磁盘格式流式传输对象,因此无需花时间计算客户端缺少哪些对象,也无需执行增量压缩。当缓存缺少某个对象时,它会向权威的 scion-servers 请求,后者会根据需要从 Spanner 或云存储中读取数据。
scion-daemon、scion-cache 和 scion-server 对此协议中的相同 RPC 都会作出响应,因此客户端无需了解缓存拓扑。理论上,任何层都可以与其他任何层通信,但我们会严格执行分层,以隔离故障域。我们希望避免这样的情况:某个区域的 scion-cache 出现故障,导致上游 scion-server 不堪重负,进而使整个系统瘫痪。

Scion 的代码检出架构。
该系统的每一层缓存命中率都超过 99%,且不需要长期状态,因此有需要时可以轻松扩容。按 CPU 和内存需求计算,我们现在的 Scion 基础设施规模比以前的 git 镜像基础设施小了 200 倍。CI 和研究任务检出代码的速度提高了两个数量级(从几分钟缩短到几秒),为整个集群腾出了大量容量;仅 CI 就节省了 25% 的 CPU 容量。客户端和缓存都无需执行仓库维护,这简化了任务的生命周期管理。
构建 Scion 的经验
我们开始构建 Scion 的时间,正好与 Claude Mythos Preview 和 Claude Tag 在内部开放使用的时间相近。这两项发布同时加大了对 Scion 的需求, 也显著加快了我们的构建速度。
年初,我们基本上是手工编写了 git 到 Scion 的摄取流水线的第一个原型版本。这时有个特别引人注目的例子。我们原本觉得它已经不错了——直到拿到 Claude Mythos Preview 的早期内部版本。我们让它重写整个流水线,没有提供任何额外指示,它就生成了一个内存占用减少 20 倍、速度提升 4 倍的版本,同时保留了有关摄取顺序和数据结构的所有隐含不变量。

2026 年 1 月至 9 月期间,Scion 和 Antique 代码库每周合并的变更数量。
构建和运维 Scion 的经验,为构建智能体规模的软件可能是什么样子,提供了一些有趣的启示。
小团队快速开发将越来越普遍。一个由七人组成的团队在五个月内构建了 Scion 和 Antique(我们的代码审查工具,我们完全可以再专门写一篇博客介绍它),还开发了另外六个相关系统,并完成了全面迁移。与此同时,每天合并的代码量增长了 10 倍。过去,开发和部署这样一套源代码管理系统,需要公司经过数年迭代迁移,并投入更多工程师。我们认为,未来这种快速、灵活的迁移方式将越来越普遍。
工程师的价值并未降低,但他们的工作正在改变。参与开发这套系统的所有工程师如今都会认为,Claude 在编写代码方面比自己更出色。但我们的工程师仍然提供了不可或缺的理解:把握系统间组件的高层结构和设计层面的权衡,围绕我们希望解决的问题以及我们明确需要达到的规模,作出明智的选择。我们经常发现,工程师的角色是对 Claude 说“不”。智能体有时会违反系统的核心约束,去满足一种原本就不该被允许的模式。这在我们的迁移过程中尤为常见。例如,有多个系统试图通过线性读取提交日志,以蛮力查找某项变更何时合并到 main,而不是查询我们的代码审查工具。Anthropic 也广泛采用对抗性审查,虽然这种方法在许多方面极其有效,但也可能目光短浅,因此需要经验丰富的工程师引导 Claude,避免它为了应对假设性的担忧而对系统的某些部分进行过度设计。例如,有一次这样的审查对信任另一个内部服务过于谨慎,导致 Claude 添加了过多的校验,增加了复杂性。我们的团队成员仍然会审查系统的每一项变更请求,但他们现在考察的是变更的意图,而不是逐行阅读代码。
我们得以大幅提升优化水平和质量。得益于 Claude 能够以远超我们的速度编写海量代码,Scion 成为我们迄今参与过的项目中,测试最充分、防护最周全的代码库之一。即便时间紧迫,我们仍然为曾遇到的每一种非简单的基础设施交互和版本控制交互构建了完全隔离的模拟环境,还实现了模糊测试和精细的速率限制。Claude 还轻而易举地找到了显著的优化机会——例如,对一个未命中我们某个索引的祖先关系查询进行算法优化——这些工作原本需要专家花上数天乃至数周进行分析和调优……而第二天,它又把这一切做了一遍。

在原生 Scion 仓库上观察到的性能提升。
我们为单体仓库安全的新形式奠定了基础。在庞大的单体仓库中,管理安全性、信息安全和合规性,与管理其体量和规模同样重要。我们从一开始就在构建 Scion 时将这些因素纳入考虑。它支持丰富的仓库访问控制,也支持对单个目录设置权限,从而在不失去单体仓库优势的前提下保护最敏感的代码。此外,我们构建的代码审查工具能够为审批表达复杂的语义约束,使我们可以区分仅修改注释这类简单变更与敏感的逻辑变更,让人工审查者能够专注于影响最重大的变更。
实现更高水平的卓越运营已成为可能。 我们的团队大量使用 Claude Tag,它帮助我们监控系统的每一个组件。例如,我们创建了一些 Slack 频道,要求 Claude 全神贯注地关注某一个组件的健康状况。我们把这些 Claude 称为“超级粉丝”。我们有 scion-daemon 超级粉丝、scion-cache 超级粉丝、Antique 合并队列超级粉丝,等等。它们可以访问我们的遥测数据和日志,表现得就像一位经验丰富的工程师,除了不断刷新图表、寻找异常之外,别无他事可做。我们也为所有系统配置了传统告警,但我们的超级粉丝能发现细微的新型问题,相比之下,这些告警就显得相当单一。
我们正在彻底重新审视原有的假设。 这个项目促使我们非常认真地审视其他为人类工作规模设计的工作流程。例如,在智能体的工作规模下,一套规范的合并队列就不太有意义了。当测试运行期间可能有数百甚至数千项变更被合并时,CI 究竟意味着什么?我们一直未能有效复现业界标准的做法,即为合并队列设置全局仓库锁,因此转而投入资源,开展合并后和部署前的自动化测试。这确实意味着并发变更可能发生冲突,但 Claude 通常能在人工发现这些冲突之前就将其修复。
为更广泛的生态系统作出贡献
能力日益增强的 AI 智能体引发了规模扩展方面的担忧,我们认为 Scion 只是应对这些问题的方案之一。我们分享这些细节,是为了展示团队如何应对类似挑战,但我们并不认为我们的解决方案或考量适用于所有情况。
Scion 绝不是适合通用场景的 git 替代品。我们在这里概述的概念,实际上只对真正庞大的单体仓库有意义:要么有数万名工程师参与开发,要么有大批智能体在大量编写代码。此外,Anthropic 的独特之处在于,我们在内部使用中消耗 token 的能力,远超其他任何企业实际愿意投入的程度。我们这样做,既是为了满足自身的软件开发需求,也是为了测试 Claude 的能力。
从实际角度来看,很难以一种对其他组织普遍有用的形式提供 Scion。它与 git 差异很大,这意味着企业需要迁移开发生命周期的每一个环节,而且它还需要配套的代码审查、代码搜索和 CI。我们内部版本的这些组件都是高度定制的,因此其他组织很难采用。我们也担心,试图构建一个满足所有人需求的公开版本,实际上并不会有多大用处。
不过,我们预计,在未来 6 到 12 个月内,这类问题会变得更加普遍;对于每一家使用 git 的公司来说,独自应对类似挑战都会低效地消耗工程资源。因此,我们认为最有价值的做法是分享我们的思路,让其他开发者在不偏离整体模式的前提下,将 Scion 中一些最成功的部分应用到 git 上。git 服务器不必像 git 客户端那样工作;新的服务器架构已经能够显著提升可扩展性。Git 平台可以像 Scion 一样提供全面的服务器端 RPC,甚至可以在各个代码托管平台之间采用标准化方式,让 git 客户端能够与之通信。协商协议也不必完全沿用现有形式,git 中各种稀疏检出和浅克隆功能也可以更好地协同运作。
最后,我们要感谢 git 社区近二十年来为支持数百万开发者而持续扩展这一平台。我们的贡献范围要小得多:分享我们对智能体式软件开发如何与这些系统交互的初步看法,以便与 GitHub、GitLab 和开源软件社区携手,满足开发者不断变化的需求。随着软件开发生命周期与 AI 的指数级发展相交汇,我们都在同一条船上。
来源:Claude Blog · anthropic.com