Hacker News Best

Redesigning source control at Anthropic

8.5内容质量

TL;DR · AI 摘要

Anthropic因AI驱动的代码生成量激增,重构源码控制系统,开发Scion系统应对日均3万次代码合并的挑战。

核心要点

  • Scion系统在5个月内完成从git到自研版本控制系统的迁移
  • 日均代码合并量从2500次激增至30000次,传统git方案无法承载
  • 主分支历史数据达100GB,每日CI代码检出量达2500万次

结构提纲

按章节快速跳转。

  1. Anthropic因AI生成代码量激增,传统源码控制系统成为瓶颈。

  2. git在超大规模代码库场景下面临性能和存储的极限挑战。

  3. ›Scion系统设计

    自研版本控制系统通过新型数据结构优化存储和检索效率。

  4. 5个月内完成从git到Scion的全量迁移并支撑业务增长。

  5. 采用增量存储、智能分片等技术降低数据冗余度。

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • 重构源码控制系统
    • 挑战
      • git存储瓶颈
      • CI流量压力
    • 解决方案
      • Scion系统设计
      • 迁移实践
    • 成果
      • 5个月完成迁移
      • 支撑3万次/日合并

金句 / Highlights

值得收藏与分享的关键句。

#源码控制#Scion#AI编码#Anthropic#版本控制系统
打开原文

在Anthropic重新设计源代码控制 \ Anthropic

在Anthropic,编码已不再是构建系统或进行研究的瓶颈。Claude能够自主迭代研究流程、对复杂程序进行微优化,并以前所未有的速度和规模从零开始构建新服务。现在,瓶颈变成了所有为人类规模构建的开发者基础设施:源代码控制、构建和持续集成。

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

2026年1月至9月每天合并到我们单体仓库的代码更改量。

为了支持这种增长,我们开发了Scion——一个内部版本控制系统,并在五个月内完成了从git向单体仓库的全面迁移。同时,我们还构建并推出了代码审查界面(名为Antique)、代码搜索、丰富的源代码控制IAM系统、管理及部署大量已审查代码的新方法等,所有这些都在相同的时间段内完成。在本文中,我们将重点介绍Scion的构建过程。这是一个有趣的案例研究:Claude编写代码的能力既是让我们陷入这种局面的根源,也是我们能够快速构建高质量、可扩展系统来应对它的关键。

扩展我们的单体仓库

在探讨我们构建的内容之前,我们需要承认我们的工作负载有多么不寻常。不出所料,Anthropic比几乎所有其他公司都更频繁地使用Claude编写软件,因此我们通常是最早发现为人类规模构建的工具开始出现压力的公司。源代码控制只是其中一个例子。虽然我们预计短期内不会有太多组织面临这些问题,但我们认为,随着更多使用AI编码工具的组织出现,这些压力点很快也会出现在它们身上,这就是我们想要分享我们的构建成果和经验的原因。

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

随着我们的单体仓库不断增长,它越来越不符合这种设计空间。仅主分支的历史记录就接近100GB,压缩后的工作副本超过25GB。在计算开发者环境、代理沙箱和研究任务之前,我们每天要执行约2500万次代码检出用于持续集成。如果以最简单的方式获取,这将导致每天超过600PB的git流量。

Git提供了处理仓库片段的工具,例如浅层克隆、稀疏检出和承诺远程。除此之外,还有针对大型单体仓库的标准解决方案,如内部git镜像和存储在blob存储中的仓库快照(种子)。我们使用了所有这些方法——在高峰期,我们运营了数百个内部git镜像来应对负载。这为我们赢得了时间,但随着合并速度的提高,这些方法变得越来越难以维护。种子在刷新间隔期间与主分支的顶端越来越远,保持依赖于历史记录的操作(如差异和追溯责任)在部分克隆上正常运行需要越来越多的维护。

扩展git检出的典型设置。

回顾整体情况,我们意识到与大多数公司一样,我们最初也是将git作为集中式系统使用,依赖一个权威副本。但几乎所有的客户端都是按定义必须在线的代理和任务。使git在分布式、离线工作中表现出色的特性,随着仓库规模的增长,反而在削弱我们扩展能力。于是我们开始思考:如果我们从一开始就将代理始终在线这一事实纳入源代码控制系统的设计,会是什么样子?

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

在代理编程范式下,许多关于软件开发的常见假设正在发生演变。我们在构建Scion时拥抱了这些变化,这使我们能够在多个核心原则上采取与git截然不同的方法。大型单体仓库的源代码控制已有先例,例如Google的Piper和Meta的Sapling,因此我们能够借鉴它们的经验教训来构建Scion。我们的设计原则包括:

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

客户端-服务器非对称性 在Git中,每个客户端都具备完整服务器功能。在Scion中,服务器负责处理文件历史、归因分析、提交创建、合并和变基等操作。客户端则维护一个有限的本地对象缓存,将工作副本文件布局在磁盘上,监控文件系统变更,并为用户提供与服务器交互的友好方式。客户端自身计算能力有限,但因此本地所需数据量也大幅减少。

全面的源代码控制服务可驱动创新应用 在Git中,如果CI任务或工具需要查询仓库信息(如某行代码最后由谁修改,或两个提交之间的关联关系),必须维护本地仓库副本,或依赖厂商特定API提供答案。在Scion中,客户端使用的所有源代码控制查询都通过API暴露,因此许多任务根本不需要本地仓库。像我们自动二分查找和回滚流水线这样的内部工具,若没有这种低成本的程序化源代码控制访问能力,在大规模场景下将难以实现。

所有组件必须水平可扩展 在高峰期,我们曾运行数百个Git镜像,这显著消耗了资源容量,当仓库规模超过镜像的磁盘或内存限制时扩展能力就消失了。在Scion中,仓库数据是分布式的,每个组件都可独立扩展;系统中任何地方(包括服务器端)都没有仓库的完整物理副本。由于不存在Git的打包文件(对象压缩包),因此无需进行仓库维护。

提交操作必须具备低成本、增量式和无锁特性。在 Git 中,用户本地提交后需要推送到服务器以共享工作成果。而在 Scion 中,所有提交、变基或合并操作均在服务器端完成,因此无需推送操作,但这些操作必须保持高效。创建提交和更新下游数据结构的开销应随更改规模增长,而非整个仓库的规模,否则服务器将无法应对。Scion 的开发周期设计倾向于减少引用(指向提交的指针,如分支和标签),仅在主引用或发布分支中保留,因为所有服务器都需要达成一致的全局状态会带来显著的扩展性挑战。

工作副本是可选的,并且默认采用稀疏设计。由于我们的单体仓库规模庞大,且每天需要进行数千万次 CI 构建的代码检出,我们尽可能避免执行检出操作。在 Scion 中,例如 CI 任务需要获取某个提交与其合并基的差异时,可以直接向服务器请求。写入操作同样可以跳过检出,通过 API 完成。Scion 的协议围绕稀疏检出和虚拟化检出构建:稀疏检出仅包含客户端请求的目录,虚拟化检出则按需生成文件。这样,当任务确实需要访问磁盘文件时,客户端只需为实际读取的内容付费。

Scion 的工作原理

理解 Scion 的构建过程中这些原则如何实际结合在一起具有重要意义。这些洞察不仅适用于其他源代码控制系统,也可能对软件开发生命周期的其他阶段产生启发。

数据结构

在 Scion 中,工作的基本单位是“变更集”(changeset),即预写日志(write-ahead log)中的条目。变更集存储了 Git 提交中大多数预期内容。但与 Git 提交存储树指针不同,变更集存储了相对于其第一个父节点的文件变更列表,以及指向 blob 存储中压缩文件内容的内容寻址指针。变更集在服务器端创建,且可立即在所有位置访问。

Scion 中变更集的结构。

这种结构使我们能够以极高的吞吐量接受变更集,因为无需处理包文件、无需锁定或跨服务器协调,也无需移动引用。Scion 技术上支持引用,但由于创建和获取提交均不涉及引用,移动引用的操作是针对单个 Spanner 行的比较并交换操作,与创建变更集的过程分离。

变更集仅记录变更内容,因此单独的变更集无法快速回答“谁最后编辑了某一行”或“哪些提交影响了某个文件”等问题。为解决这些问题,Scion 会从变更集中构建额外的数据结构,包括专门用于文件归因和路径历史的结构,以及用于提交图的跳表。我们称所有不直接属于变更集的内容为“派生数据”,并通过一种通用机制计算所有派生数据。每种派生数据都递归定义,因此其变更集的值由其父变更集的值计算得出。

该机制具有三个优势。首先,当新的数据结构能够使某些操作更高效时,我们可以轻松添加新的派生数据类型。其次,针对每种类型,我们可以选择是立即计算还是延迟执行,从而避免生成不需要的数据。第三,由于客户端永远无法看到这些数据结构的内部实现,我们可以在不修改客户端的情况下对它们进行优化。随着系统的发展,我们多次添加了新的派生数据类型,例如用于复制检测或基于路径的文件搜索的新索引。

扩展 Scion

少量 Scion 服务器即可为客户端提供所需的任何源代码控制 API,例如获取变更集、计算差异、查找合并基础和获取 blob 数据。所有源代码控制数据均采用内容寻址方式,因此一旦数据存储后将永不更改,任何服务器都可以在不与其他服务器协调的情况下缓存这些数据。因此,这些服务器具有水平扩展能力且协调需求极少。本质上,它们是大型缓存,不仅能实时计算派生数据,还能将结果写回 Spanner。

Scion 的众多使用者不再需要维护自己的本地仓库。过去,获取和维护仓库曾是 CI 作业或需要查询源代码控制信息(尤其是提交历史和 blame 信息)的工具的重大维护负担。在迁移初期,当我们还没有自己的客户端、仓库格式或协议时,Scion 仅负责回答这些“元数据”查询。获取文件内容和生成工作副本是数据密集型操作,需要独立的设计方案。

Scion 的 scn 客户端在多个重要方面与 git 的 fetch 协议存在差异,因此能够实现高度可扩展的架构,且 CPU 使用率远低于 git 服务器。Scion 的协商机制不会告诉服务器客户端已拥有的内容,而是完全基于客户端需要的树和 blob 进行操作。从根树开始,客户端会检查本地存储中缺失的树并递归请求,然后重复该过程以获取 blob。每个请求都是针对一个永远不会改变的特定对象,因此任何缓存都可以响应,架构与传统的分层缓存非常相似。该过程也高效且可恢复,因为不存在单个数 GB 的 packfile 响应。稀疏甚至虚拟化检出无需特殊支持,因为客户端只需忽略不需要的树即可。

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

Scion 的 scion-daemon、scion-cache 和 scion-server 针对这一协议都响应相同的 RPC,因此客户端无需了解缓存拓扑结构。理论上任何层级都可以与其他层级通信,但我们严格实施层级划分以隔离故障域。我们希望避免区域 scion-cache 故障导致上游 scion-server 过载并引发整个系统崩溃的情况。

Scion 的 checkout 架构。

该系统的每一层级都保持超过 99% 的缓存命中率,且无需维护长期状态,因此如需扩展非常容易。我们的 Scion 基础设施在 CPU 和内存需求方面,规模仅为之前 git 镜像基础设施的 1/200。持续集成(CI)和研究任务的代码检出速度提升了两个数量级(从分钟级缩短到秒级),这释放了舰队中大量资源容量;仅 CI 单项就实现了 25% 的 CPU 容量提升。客户端和缓存都不需要执行仓库维护,这简化了我们任务的生命周期管理。

构建 Scion 的经验教训

我们开始构建 Scion 的时间,与 Claude Mythos Preview 和 Claude Tag 内部发布的时间大致相同。这两个版本的发布同时加剧了对 Scion 的需求,并显著加快了我们构建 Scion 的速度。

这里有一个特别引人注目的例子。年初时,我们基本手动编写了 git-to-Scion 数据管道的第一个版本作为原型。我们原本认为这个实现已经不错,直到接触到 Claude Mythos Preview 的早期内部快照。我们让它重写整个实现,没有进一步指示的情况下,它生成的版本内存消耗减少了 20 倍,速度提升了 4 倍,同时保留了所有关于数据摄入顺序和数据结构的隐式不变量。

2026 年 1 月至 9 月每周合并到 Scion 和 Antique 代码库的更改量。

构建和运营 Scion 的经验为大规模构建代理软件提供了有趣的启示。

小团队快速开发将成为更常见的模式。一个七人团队不仅构建了 Scion 和 Antique(我们的代码审查工具,我们甚至可以为此写另一篇博客文章),还开发了六个相关系统,并在五个月内完成了全面迁移。我们做到这些的同时,每日合并的代码量增加了 10 倍。过去,开发和部署这样的源代码控制系统需要公司数年的迭代迁移,并需要更多工程师参与。我们认为这种快速、灵活的迁移方式在未来将变得更加普遍。

工程师的价值并未降低,但他们的工作方式正在发生变化。所有曾参与该系统开发的工程师现在都会认为Claude在编写代码方面比他们更优秀。但我们的工程师仍然提供了对高层级系统组件和设计层面权衡的宝贵理解,他们能够根据我们想要解决的问题和需要实现的规模,做出明智的选择。我们经常发现,工程师的职责之一就是告诉Claude“不”。有时代理会违反核心系统约束以满足本就不应被允许的模式。这种情况在我们的迁移过程中尤为常见。例如,有多个系统试图通过线性读取提交日志来暴力查找变更何时合并到主分支,而不是询问我们的代码审查工具。Anthropic还广泛采用对抗性评审,尽管这在许多方面都非常有效,但也可能导致短视,因此需要经验丰富的工程师引导Claude避免因应对假设性担忧而过度设计系统。例如,有一次评审对信任另一个内部服务过于防御性,导致Claude添加了过多的验证和复杂性。我们的团队成员仍然会审查系统中的每个变更请求,但现在他们审查的是变更的意图,而不是逐行阅读代码。

我们能够显著提升优化效果和代码质量。得益于Claude能够以远超我们自身速度编写大量代码的能力,Scion是我们有史以来测试最充分、防御最完善的代码库之一。即使在紧张的时间表下,我们仍然能够构建每个非平凡基础设施和源代码控制交互的严格模拟,以及模糊测试和复杂的速率限制机制。Claude还轻松发现了多项重要优化,例如一个算法优化修复了祖先查询中遗漏的索引,这本需要专家花费数天或数周的分析和调优,而Claude在第二天就重新完成了所有工作。

原生Scion仓库的性能改进观测结果。

我们为新型单体仓库安全模式奠定了基础。在大规模单体仓库中管理安全、合规性与规模同样重要。我们从一开始就将这些需求纳入Scion的设计。它支持对仓库的丰富访问控制,同时能够对单个目录应用权限,从而在不牺牲单体仓库优势的前提下保护最敏感的代码。此外,我们构建的代码审查工具可以表达复杂的语义审批约束,使我们能够区分简单的注释变更与敏感的逻辑变更,让人类审查者专注于最关键的变化。

实现运营卓越的新层次已成为可能。我们的团队大量使用Claude Tag,这帮助我们监控系统的每个组件。例如,我们创建了Slack频道,让Claude专注于监控单个组件的健康状况。我们称这些Claude为"超级粉丝",包括scion-daemon超级粉丝、scion-cache超级粉丝、Antique合并队列超级粉丝等。它们可以访问我们的遥测数据和日志,就像一位无所事事但不断刷新图表、寻找异常的资深工程师。我们为所有系统都设置了传统告警,但这些超级粉丝能够发现传统告警难以察觉的细微和新颖问题。

我们正在彻底重新审视原有假设。这个项目促使我们深入审视为人类规模设计的工作流程。例如,一个完善的合并队列在代理规模下就显得不太合理。当数百甚至数千个变更在测试运行时就能合并,此时CI(持续集成)的含义又是什么?我们尚未能有效复制行业标准的合并队列全局仓库锁定机制,因此转而投资于合并后和部署前的自动化测试。这确实意味着并发变更可能发生冲突,但Claude通常能比人类更快识别并解决这些冲突。

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

我们将Scion视为应对日益强大的AI代理带来的扩展问题的解决方案的一部分。我们分享这些细节作为团队应对类似挑战的示例,但不认为我们的解决方案或考虑适用于所有情况。

Scion绝不是通用场景下git的合适替代品。我们在此阐述的概念仅适用于真正庞大的单一仓库,这些仓库要么有数万名工程师,要么有大量代理持续编写代码。此外,Anthropic的独特之处在于,我们用于内部使用的令牌消耗能力远超其他企业可能实际需要的水平。我们这样做既为满足自身软件开发需求,也为测试Claude的能力。

从实际角度来看,很难以对其他组织具有广泛实用价值的形式提供Scion。它与git存在显著差异,这意味着公司需要迁移整个开发生命周期的每个环节,且需要独立的代码审查、代码搜索和CI系统。我们内部的这些组件版本高度定制化,其他组织要采用将面临困难。我们担心,试图构建一个试图取悦所有人的公共版本,实际上可能并不实用。

然而,我们预计这类问题在未来六到十二个月内将变得更加普遍,如果每个使用 git 的公司都单独面对类似挑战,这将是对工程资源的低效利用。鉴于此,我们认为最有效的事情就是分享我们的思考,使其他开发者能够借鉴 Scion 中最成功的部分,将它们应用到 git 中,而无需偏离整体模型。git 服务器的工作方式不必与 git 客户端完全相同;存在一些新颖的服务器架构,能够显著提升扩展性。git 平台可以提供类似 Scion 的全面服务器端远程过程调用(RPCs),甚至可能以标准化的方式跨不同代码托管平台实现,使 git 客户端能够学习与之交互。协商协议也不必完全沿用当前的形式,git 中的各种稀疏和浅层功能也可以被更好地组合利用。

最后,我们想承认,git 社区已经花费了近二十年的时间,为数百万开发者扩展该平台。我们的贡献范围相对有限:分享我们对代理式软件开发如何与这些系统交互的早期见解,以便与 GitHub、GitLab 以及开源社区合作,共同满足开发者不断变化的需求。当软件开发生命周期与人工智能的指数级发展交汇时,我们所有人都身处同一艘船上。

致谢

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

脚注

  • 相对于合并操作的第一个父节点存储更改文件是一种风格选择;实际上,我们发现人们在基于主干的开发(trunk-based development)的单一仓库(monorepo)中,通常只关心主线上提交的历史记录或责任归属。