Google 在 MonoRepo 上的实践
May 29, 2023
Google 在早期就使用集中式源代码控制系统管理共享代码库。也就是说,在 Google 内部,是先有的 MonoRepo,然后基于 MonoRepo 形成了一套基于主干开发的工作流程,以及支撑此流程的工具。
工作流 #
下图展示了 Google 的开发人员,在日常研发过程中,例如代码编写、代码评审、代码分析、代码重构等,所使用的工具链,以及工具之间的协作关系。值得注意的是,工具链全都基于云,要求开发人员在线使用。
从上往下看,处于最中间位置的系统 Piper,它是一个源代码管理系统。
Piper:源代码管理系统 #
Piper 基于 Google 标准基础设施实现,分部在全球 10 个数据中心,依赖 Paxos 算法保证副本间的一致性。这种架构提供了高水平的冗余能力,加上缓存和异步操作,屏蔽了大部分网络延迟。
Piper 支持文件级别的访问控制,对于配置文件、算法文件有着更严格的控制。此外,对 Piper 中的文件的读写访问,均有审计日志。对于错误提交的敏感信息,不仅可以清除相关文件,还可以查阅日志判断,是否存在敏感信息泄漏风险。
在 Piper 工作流中,开发在修改文件之前,在本地创建一份仓库的副本,这些文件保存在开发的工作区内。根据需要,可以将 Piper 的代码的变更合并到工作区,也可以共享工作区给其他人,并请求代码评审。只有评审通过的代码才能合入主库。
有了源码管理系统,对于研发来说,首先要做的事情,就是将代码克隆到本地,我们日常使用的是 git,在 Google 的大库体系下,有一个专门对接 Piper 的客户端工具——CitC。
CitC:客户端工具 #
CitC,全称是 Clients in the Cloud,类似 Git 的客户端工具。基于云存储和一个仅限 Linux 的 FUSE 文件系统组成。不需要在本地先克隆代码即可浏览代码。开发人员将他们的工作区视为文件系统的目录,只有修改过的文件才会存储在他们的工作区。这种架构,使得工作区占用的磁盘空间很小(平均每个工作区不到 10 个文件),却可以展示完整的 Piper 代码库。
所有对文件的写入都作为快照存储在 CitC 中,从而可以根据需要恢复先前阶段的工作。快照可以命名、恢复或标记。因此,只要机器连接到云存储,就可以在该机器上使用 CitC 工作区。
自动化流程利用 CitC 中未提交代码的可见性,提升开发人员的工作效率。例如:变更的代码需要 CR 时,自动发送 CR 请求;CR 被标记为完成,测试自动执行;测试通过,代码自动提交到仓库。在 CR 时,CodeSearch 工具可以对 CitC 工作区进行简单编辑,例如:修复拼写错误或修改评论等。
Google 是非常重视代码质量,因此严格的 CR 流程需要特定的工具支撑——Critique。
Critique:代码审查工具 #
代码评审员需求评审代码质量的各个方面,包括设计、功能、复杂性、测试、命名、评论质量和代码风格,如各种特定于语言的 Google 风格指南所记录。代码评审员在查看代码的变更时,可在任意行的更改发表评论。它鼓励代码评审员和代码提交人充分沟通再修改,最终评审人员留下“LGTM”,评审完成。
如果说 CR 是代码提交前的评审,那么代码合入后的检查也是至关重要的。代码的静态分析,例如测试覆盖率这类计算密集型操作,会经常触发。承担这一职责的工具,就是 Trioder。
Tricorder:静态分析系统 #
在 Google 的 CR 工具中自动提供有关代码质量、测试覆盖率和测试结果的数据。这些计算密集型检查会定期触发,以及在发送代码更改以供审查时触发。Tricorder 还提供针对许多错误的一键式代码编辑建议修复。这些系统提供重要数据以提高代码审查的有效性并保持 Google 代码库的健康。
完成了代码编写、代码评审、代码分析,并不代表就万事大吉。项目升级、代码重构、代码清理等操作,也是为了维护代码健康的重要手段。这里,Rosie 工具腾空出世。
Rosie:大规模清理和代码更改工具 #
谷歌开发团队偶尔会进行一系列影响广泛的代码清理和代码重构,以进一步维护代码库的健康。执行这些更改的开发人员通常将它们分为两个阶段。首先进行大的更改:向后兼容;完成后,可以进行第二个较小的更改:删除不再引用的老代码。
借助 Rosie,开发人员可以通过在整个存储库中进行查找和替换,或通过更复杂的重构工具来构建大型补丁。然后 Rosie 负责将大补丁拆分成小的补丁,单独测试,将它们发送出去进行 CR,并在它们通过测试和 CR 后自动提交。Rosie 沿着项目目录行拆分补丁,基于 Ownership 的层次结构将补丁发送给指定的 Code Reviewer。
成本和权衡 #
MonoRepo 不仅仅是单体软件设计,而是此模型必须要考虑的成本,以及如何权衡:
- 用于开发和执行的工具投资;
- 代码库的复杂性,包括不必要的依赖和代码检索的困难;
- 在代码健康方面投入的精力。
那么,大库模式的优点:
- 统一版本控制,SSOT;
- 代码共享和重用广泛;
- 依赖管理简化;
- 变更原子化;
- 大规模重构;
- 跨团队协作;
- 灵活的团队边界和代码所有权;
- 代码可见性和清晰的树结构提供了隐式团队命名空间。
备选方案 #
Google 也不是只钟爱大库模式,随着 Git 等分布式版本控制系统(DVCS)的普及和不断增长,Google 也考虑过是否从 Piper 迁移到 Git。例如: Android 和 Chrome 团队。
Git 社区强烈建议并希望开发人员拥有更多和更小的代码库。Git-clone 操作需要将所有内容复制到本地,这是一个与大库不兼容的过程。要迁移到基于 Git 的源代码托管,必须将 Google 的大库拆分为数千个独立的库,才能达到等同的性能。作为对比,Google 的 Android 代码库,由 Git 托管,被分为 800 多个独立的库。
考虑到从 Google 构建的现有工具中获得的价值以及整体代码库结构的诸多优势,显然转向更多和更小的库对于 Google 的大库没有意义。迁移到 Git 或任何其他需要拆库的 DVCS 的替代方案对 Google 来说并不具有吸引力。
Google 源代码团队目前的投资主要集中在内部源代码系统的持续可靠性、可扩展性和安全性上。该团队还在使用 Mercurial 进行实验,这是一个类似于 Git 的开源 DVCS。目标是向 Mercurial 客户端添加可扩展性功能,以便它可以有效地支持 Google 大库。这将为 Google 的开发人员提供另一种选择,即使用流行的 DVCS 工作流与大库结合使用。这项工作是与开源 Mercurial 社区合作进行的,其中包括来自重视大库模型的其他公司的贡献者。