Anthropic Sitemap来源发布时间

重新设计 Anthropic 的源代码控制

10月15日更新:补充了更多关于那些率先采用我们在 Scion 中使用的许多技术的项目的细节。

10月15日更新:补充了更多关于那些率先采用我们在 Scion 中使用的许多技术的项目的细节。

在 Anthropic,编写代码不再是构建系统或开展研究的瓶颈。Claude 能够自主迭代研究循环、微优化复杂程序,并以前所未有的速度和规模从零开始构建新服务。如今的瓶颈,是为人类规模而构建的开发者基础设施:源代码控制、构建和 CI。

2026年5月,Anthropic 平均每天合并约 2,500 次代码变更;截至9月,我们每天合并超过 30,000 次。我们预计这一增长速度不会很快放缓;合并代码的潜在需求几乎是无限的,而我们消除的每一个瓶颈都会立即导致我们产出的代码量进一步增加。

2026年1月至9月,每天合并到我们 monorepo 的变更数量。

为了支撑这种增长,我们构建了 Scion,一个自研版本控制系统,并在五个月内将我们的 monorepo 从 git 完全迁移过来。我们还在同一时间段内构建并交付了代码评审界面(称为 Antique)、代码搜索、丰富的源代码控制 IAM 系统、管理和合入大量已评审代码的新方法等等。在这篇文章中,我们将重点介绍我们是如何构建 Scion 的。这是一个有趣的案例研究:Claude 编写代码的能力既是最初让我们陷入这种境地的原因, 也是 我们能够快速构建一个高质量、可扩展的系统来应对它的原因。

扩展我们的 monorepo

在探讨我们构建了什么之前,我们应该承认我们的工作负载是多么不寻常。不出所料,Anthropic 使用 Claude 编写软件的程度远远超过几乎所有其他公司,因此我们往往是最早看到为人类规模构建的工具开始吃力的公司。源代码控制只是其中一个例子。虽然我们不预计短期内会有很多组织遇到这些问题,但我们怀疑用不了多久,其他使用 AI 编码工具的人也会经历同样的压力点,这就是我们想分享我们构建了什么以及学到了什么的原因。

Git 是一个去中心化的系统:每个克隆都是一个完整、自包含的仓库,可以在完全离线的情况下计算差异、读取历史和 blame,并检出任何修订版本。对于绝大多数仓库来说,这是正确的设计,也是 git 成为标准的重要原因。

随着我们的 monorepo 增长,它越来越不符合那种设计空间。仅我们主分支的历史就接近 100 GB,一个 压缩后的 工作副本超过 25 GB。我们每天为 CI 执行约 25 百万次代码检出,这还不包括开发者环境、agent 沙箱和研究任务。如果简单粗暴地获取,那将是每天超过 600 PB 的 git 流量。

Git 提供了处理仓库切片的工具,例如浅克隆、稀疏检出和 promisor remotes。除此之外,还有一些针对大型 monorepo 的标准变通方法,比如内部 git 镜像和保存在 blob 存储中的仓库快照(种子)。我们全都用过——在高峰期,我们运行了几百个内部 git 镜像来应对负载。这为我们争取了时间,但随着合并率的提高,它们变得越来越难以运维。种子在两次刷新之间越来越落后于 main 的最新提交,而让依赖历史的操作(如 diff 和 blame)在部分克隆上继续工作也需要越来越小心。

扩展 git 检出的典型设置。

退一步来看,我们注意到,与大多数公司一样,我们把 git 当作只有一个权威副本的集中式系统来使用,但我们的几乎所有客户端都是天然在线的 agent 和任务。git 在分布式离线工作方面的优秀特性,实际上正在削弱我们随仓库增长而扩展的能力。于是我们问自己:如果我们在设计源代码控制时从一开始就考虑到 agent 始终在线这一事实,它会是什么样子?

面向 agent 规模的源代码控制原则

随着 agentic 编码的出现,关于软件开发方式的许多常见假设正在演变。我们在构建 Scion 时拥抱了这些变化,这使我们能够在几个核心原则上采取与 git 根本不同的方法。大型 monorepo 的源代码控制已有先例,尤其是 Google 的 Piper 和 Meta 的 Sapling,2 因此我们得以在构建 Scion 时借鉴它们来之不易的经验。我们的原则是:

客户端始终在线。 Git 假设客户端离线工作并需要仓库的完整副本。由于我们的客户端几乎都是 agent 和任务,我们围绕一个始终可访问的服务器设计了 Scion 的客户端和协议,让客户端在需要时获取数据。

客户端-服务器不对称。 在 git 中,每个客户端都能够成为功能齐全的服务器。在 Scion 中,服务器负责回答有关文件历史和 blame 的问题、创建提交、执行合并和 rebase 等操作。客户端维护一个有界的本地对象缓存,在工作副本中布局磁盘上的文件,监视文件系统变化,并为用户提供一种符合人体工程学的方式来与服务器协作。它自己几乎无法计算任何东西,但作为交换,它在本地需要的数据要少得多。

一个功能全面的源代码控制服务可以支撑许多新颖的应用。 使用 git 时,需要询问仓库相关问题(例如某行最后由谁修改,或两个提交之间如何关联)的 CI 作业或工具,必须维护仓库的本地副本,或者只能指望特定厂商的 API 能提供答案。而在 Scion 中,客户端使用的所有源代码控制查询都通过 API 暴露,因此在许多情况下作业完全不需要本地仓库。许多内部工具,例如我们的自动化 bisect 和 revert 流水线,如果没有这种廉价、可编程的源代码控制访问方式,根本无法大规模实用化。

一切都必须可以水平扩展。 在峰值时期,我们运行了几百个 git 镜像,这严重消耗了容量,而且一旦仓库超出镜像的磁盘或内存限制,就无法继续扩展。在 Scion 中,仓库的数据是分布式的,每个组件都可以独立扩展;任何地方都不存在完全物化的仓库副本,即使是在服务器上也没有。系统中没有 packfile(git 压缩对象包)的对应物,因此无需进行仓库维护。

提交必须廉价、增量且无锁。 在 git 中,你在本地提交,然后 push 来分享你的工作。在 Scion 中,每次提交、变基或合并都发生在服务器上,因此没有东西需要 push,但这些操作中的每一个都必须快速。创建提交并更新其下游数据结构的成本必须随变更的大小而扩展,而不是随仓库的总体大小扩展,否则服务器无法跟上。Scion 的开发生命周期不鼓励使用 ref(指向提交的指针,例如分支和标签),主 ref 或发布分支除外,因为 全局 状态需要每台服务器都达成一致,这是一个潜在的扩展问题。

工作副本是可选的,并且在设计上就是稀疏的。 由于我们的 monorepo 非常庞大,而且我们每天为 CI 检出代码数千万次,因此我们希望尽可能避免检出。在 Scion 中,例如需要某个提交与其合并基之间的 diff 的 CI 作业,可以直接向服务器请求。写入操作也是如此:许多写入工作负载同样可以跳过检出,完全通过 API 完成。Scion 的协议以稀疏检出为中心,稀疏检出只包含客户端请求的目录;还有虚拟化检出,它按需物化文件,因此当作业确实需要磁盘上的文件时,客户端只需为它实际读取的内容付出代价。

Scion 的工作原理

了解这些原则在构建 Scion 时如何结合起来是很有帮助的。这些见解可以应用于其他源代码控制系统,或者更广泛地,应用于软件开发生命周期的其他阶段。

数据结构

在 Scion 中,工作单元是一个“changeset”(变更集),它是写前日志(write-ahead log)中的一个条目。changeset 存储了你对 git 中提交所期望的大部分内容。但 git 提交存储的是指向树的指针,而 changeset 存储的是相对于其第一父提交发生变更的文件列表,1 以及指向 blob 存储中压缩文件内容的基于内容寻址的指针。changeset 在服务器端创建,并立即在全球范围内可用。

Scion 中 changeset 的结构。

这种结构使我们能够以极高的吞吐量接受 changeset,因为没有需要接受的 packfile,没有锁定或跨服务器协调,也没有需要移动的 ref。Scion 在技术上确实支持 ref,但由于创建或获取提交都不涉及 ref,移动一个 ref 只是对单个 Spanner 行的比较并交换操作,该操作与创建 changeset 是分开进行的。

changeset 只记录已变更的内容,因此仅凭它本身无法快速回答诸如某行最后由谁编辑,或哪些提交影响了某个文件之类的问题。为了回答这些问题,Scion 会基于 changeset 构建额外的数据结构,包括用于文件 blame 和路径历史的专用结构,以及用于提交图的跳表(skip list)。我们把所有不直接属于 changeset 的内容称为“派生数据”,并通过一个通用机制来计算它们。每种派生数据都是递归定义的,因此它对某个 changeset 的值是由它对该 changeset 的父提交的值计算得出的。

这一机制有三个好处。第一,当新的数据结构能让某些操作更快时,我们可以轻松添加新类型的派生数据。第二,对于每种类型,我们可以选择是立即计算还是推迟计算,从而避免物化不需要的数据。第三,由于客户端永远看不到这些数据结构的内部实现,我们可以在不更改客户端的情况下对它们进行优化。随着系统的成长,我们已经多次添加新类型的派生数据,例如用于执行复制检测或基于路径的文件搜索的新索引。

Scion 的扩展

一小队 Scion 服务器为客户端所需的任何操作提供源代码控制 API,例如获取 changeset、计算 diff、查找合并基以及获取 blob。所有源代码控制数据都是基于内容寻址的,因此数据一旦存储就永不改变,任何服务器都可以缓存它而无需与其他服务器核实。因此,这些服务器可以水平扩展,并且几乎不需要协调。它们本质上是大型缓存,同时还能即时计算派生数据并将结果写回 Spanner。

Scion 的众多用户不再需要维护自己的本地仓库。过去,获取和维护仓库对需要向源码控制系统提问的 CI 任务或工具来说是一项沉重的维护负担,尤其是关于提交祖先关系和 blame 的问题。在迁移早期,在我们拥有自己的客户端、仓库格式或协议之前,Scion 只回答这些“元数据”查询。获取文件内容和物化工作副本是数据密集得多的操作,需要单独的设计。

Scion 的 scn 客户端在几个重要方面不同于 git 的 fetch 协议,因此适合一种高度可扩展的架构,所需 CPU 显著少于 git 服务器。Scion 的协商过程不会告诉服务器客户端已经拥有什么;相反,它只针对客户端想要的树和 blob 进行操作。从根树开始,客户端检查其本地存储中缺失的树并递归请求它们,然后对 blob 重复这一过程。每个请求都是针对一个永不改变的特定对象,因此任何缓存都能回答它,该架构非常类似于传统的分层缓存。这一过程也是高效的且可恢复的,因为不存在单个数 GB 的 packfile 响应。稀疏检出甚至虚拟化检出都不需要特殊支持,因为客户端根本不会请求它不想要的树。

在集群中,客户端通过套接字与一个守护进程 scion-daemon 通信,后者为整个节点存储对象。我们的大多数节点都需要访问 monorepo,同一节点上的作业会重用大部分相同的对象。每个守护进程从一个定期刷新的快照开始,然后只与其所在区域中称为 scion-cache 的树和 blob 缓存服务器通信。这些缓存服务器以守护进程将消费的磁盘格式直接从内存流式传输对象,因此它们无需花费时间计算客户端缺失的对象或执行增量压缩。当缓存缺少某个对象时,它会向权威的 scion-servers 请求,后者根据需要从 Spanner 或云存储读取。

scion-daemon、scion-cache 和 scion-server 都响应该协议的相同 RPC,因此客户端无需了解缓存拓扑。理论上任何一层都可以与任何其他层通信,但我们严格强制分层以隔离故障域。我们希望避免某个区域 scion-cache 的故障压垮上游 scion-servers 并导致整个系统宕机的情况。

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 仓库上观察到的性能提升。

我们为新型 monorepo 安全奠定了基础。 在庞大的 monorepo 中管理安全性、安全防护和合规性,与管理的规模体量同样重要。我们从一开始构建 Scion 时就考虑到了这些。它支持对仓库的丰富访问控制,并且能够对单个目录应用权限,从而在不失去 monorepo 优势的情况下保护最敏感的代码。此外,我们构建的代码审查工具可以在审批上表达复杂的语义约束,这让我们能够区分仅修改注释的简单变更与敏感的逻辑变更,使人类审查者可以专注于最重要的变更。

一种全新的卓越运营水平成为可能。 我们的团队大量使用 Claude Tag,这帮助我们监控系统的每个组件。例如,我们创建了 Slack 频道,指派 Claude 执着地专注于单一组件的健康状况。我们把这些 Claude 称为“超级粉丝”。我们有一个 scion-daemon 超级粉丝、一个 scion-cache 超级粉丝、一个 Antique 合并队列超级粉丝,等等。它们可以访问我们的遥测数据和日志,表现得像一位经验丰富、闲来无事只会刷新图表并寻找任何异常的工程师。我们所有的系统也有传统警报,但我们的超级粉丝能够检测到微妙而新颖的问题,相比之下这些警报显得颇为单一。

我们正在彻底重新审视我们的先验假设。 这个项目促使我们认真审视其他为人类规模设计的流程。例如,一个规范意义上的合并队列在智能体规模下意义不大。当数百甚至数千个变更可以在测试运行期间合并时,CI 究竟意味着什么?我们未能有效复制行业标准中合并队列上的全局仓库锁,因此我们转而投资于合并后和部署前的自动化测试。这确实意味着并发的变更可能产生冲突,但 Claude 通常能比人类识别出这些冲突更快地修复它们。

为更广泛的生态系统做出贡献

我们将 Scion 视为应对日益强大的 AI 智能体所引发的扩展性难题的答案之一。我们分享这些细节,作为团队如何应对类似挑战的一个示例,尽管我们并不期望我们的解决方案或我们的考量适用于每一种情况。

Scion 绝不是可以在一般用途上替代 git 的合适选择。我们在这里阐述的概念实际上只对真正庞大的 monorepo 才有意义——要么拥有数以万计的工程师,要么有大量智能体源源不断地编写代码。此外,Anthropic 的独特之处在于,我们为内部使用而消耗的 token 能力远远超过任何其他企业现实中愿意花费的水平。我们这样做既是为了满足自身的软件开发需求,也是为了测试 Claude 的能力。

实际上,很难以对其他组织广泛有用的形式提供 Scion。它与 git 有显著差异,这意味着企业需要迁移开发生命周期的每一个环节,而且它需要自己的代码审查、代码搜索和 CI。我们的这些组件的内部版本高度定制,因此其他组织很难采用,我们担心试图打造一个取悦所有人的公开版本实际上并不会非常有用。

然而,我们预计这类问题在未来6到12个月内会变得更加普遍,对于每个使用 git 的公司来说,独自面对类似的挑战将是对工程资源的低效使用。鉴于此,我们认为最有影响力的 做法是分享我们的思考,让其他开发者能够在不脱离整体模型的情况下,将 Scion 中最成功的部分应用到 git 上。git 服务器不必以与 git 客户端相同的方式工作;已经存在能够提供显著扩展性改进的新型服务器架构。Git 平台可以像 Scion 那样提供全面的服务器端 RPC,甚至可能以跨代码托管平台的标准化方式提供,供 git 客户端学习对接。协商协议也不必与今天的样子完全一致,而且 git 中的各种稀疏和浅克隆特性可以被设计为良好地协同工作。

最后,我们想感谢 git 社区近二十年来为数百万开发者扩展该平台所做的努力。我们的贡献要狭窄得多:分享我们对智能体软件开发如何与这些系统交互的早期见解,以便我们能够与 GitHub、GitLab 和开源社区合作,支持开发者不断变化的需求。当软件开发生命周期与 AI 指数级增长交汇时,我们所有人都在同一条船上。

脚注

01相对于合并的第一个父提交存储变更文件是一种风格上的选择;在实践中,我们发现人们只关心 monorepo 中基于主干开发模式里主线提交的历史或 blame。

02具体来说是 Sapling 的服务器组件, Mononoke。它开创了我们在 Scion 中使用的许多技术,尤其是 Bonsai 数据模型。

作者与致谢

致谢

本文由 Nick G. 和源代码管理团队撰写:S.G.、R.K.、Dan C.、Garrett H.、Ben L.、Colin M.、Joe P.。我们感谢 Vilas J. 和 Sergiu D. 在这段雄心勃勃的旅程中对源代码管理团队的支持,也感谢 Amir O. 对 Antique 的早期贡献。

原始出处

Anthropic Sitemap

内容说明

原始发布及相关权利归来源方。

机器翻译 · 请以原文为准