本文译文转载自《软件工程研究与实践》,由
江贺(大连理工大学)、印莹(东北大学)、康雁(云南大学)翻译,张昱(中国科学技术大学)审核 ,吴穹博士在译文基础上做了进一步的审校
点评:下面这篇文章非常好的阐述了Google一个重要的软件工程实践 - 对所有员工开放的单一代码库。在进入正文之前,我觉得有责任让读者先明确,Google单库及主干开发方式极大地受益于Google的工程师文化、强大的质量保证能力和基础设施保证能力。读者看到这一美好愿景之后,也需要充分地评估自身文化与能力,不能简单照搬。单库、主干开发以及功能开关等实践更多是上述这些文化和能力的结果,而不是产出这些文化和能力的原因。
本文概述了代码库的规模,并详细介绍了Google定制的单一资源代码库以及选择该模式的原因。Google使用自主开发的版本控制系统来管理其大型的代码库,并使得该代码库可以被公司的大部分软件开发人员查看和使用。这种集中式系统是许多Google开发人员工作流程的基础。本文介绍了在如此大型的代码库中进行可行管理和高效工作的系统和工作流程的相关背景。本文阐释了Google“基于主干开发(trunk-based development, TBD)”的策略和用于组织工作流和保证Google的代码库运转良好的支持系统,该支持系统包括用于静态分析、代码清理以及精简化代码审查的软件。
1. Google级规模
在全球范围内的Google开发者中,有95%的人使用Google的单一软件仓库,这个软件仓库俨然已经是一个超大规模[4]的系统,这也佐证了这样一个事实:这种单个源码仓库(single-source repository)的模型具有极好的可伸缩性。
Google代码库包含大约十亿个文件,以及将近3500万次提交的历史,跨越了Google
整个18年的存在。这个代码库包含86TB的数据【a】,其中包括900万个独立源文件中约20亿行的代码。文件的总数还包括被复制到发布分支中的源文件、在最新修订版本中被删除的文件、配置文件、文档和支持数据文件;下表是来自2015年1月Google代码库的统计信息。
2014年,每周在Google代码库中大约有250,000个文件中的将近1500万行的代码被修改【b】。Linux内核是一个大型开源软件代码库的典型例子,它包含40,000个文件、约1500万行代码。【14】
Google的代码库被位于世界各国的几十个办事处的超过25,000多名Google软件开发人员所共享。在一个典型的工作日,他们对代码库提交了16,000次变更,另外有24,000次变更是由自动化系统提交的。每天代码库会收到约数十亿个文件读取请求,高峰时每秒接近800,000次查询,每个工作日大约平均每秒处理接近50万次查询。大多数流量来自于Google的分布式构建和测试系统。
图2为2010年1月至2015年7月期间,每周对主代码库提交的不同提交者的人数。图3为同一时间段内每周向Google主代码库提交的提交次数。总的提交次数对应的那条线包括交互式用例或人以及自动化用例产生的变更量。两幅图中较大的下降发生在影响大量员工的节假日(如圣诞节和元旦,美国感恩节和美国独立日等)。【c】
2012年10月,Google的中央代码库添加了对Windows用户和Mac用户的支持(之前只支持Linux),并且将现有的Windows和Mac代码库合并到主代码库中。Google用于代码库合并的工具会将所有历史修改算到原提交者名下,因此在图2中有相应的凸起,这种合并带来的影响在图1中也很明显。
根据每周提交的图表可见,直到2012年提交主要由人工主导,在这一年,Google 切换到一个定制版的中央代码库,这个会在后面讨论。在此次切换之后,提交率持续增长,主要归因于自动化提交。
管理如此大规模的代码库和对应活动对Google来说一直是挑战。尽管经过几年的实践,Google仍然无法找到一个商用或开源的版本控制系统可以支持这样大规模的单一代码库。于是Google自己开发了一个专有系统来储存、版本化并呈现源代码,这一专属系统被命名为Piper。
2. 背景
在评论用单个代码库工作的利弊之前,需要先介绍Google工具化和工作流程的背景。
Piper和CitC
Piper储存了一个单独的代码库,它实现在Google的标准基础设施之上,这个基础设施最初是Bigtable【2】,现在是Spanner【3】。Piper分布在世界各地的10个Google数据中心,依靠Paxos【6】算法来保证副本的一致性。此架构提供了高级别的冗余,以帮助Google软件开发人员优化延迟,而不论他们在哪里工作。此外,缓存和异步操作让开发人员感受不到大部分的网络延迟。这很重要的,因为要想充分利用Google基于云的工具链就需要开发人员保持在线。
Google之前依赖一个托管在一台单机上的单实例Perforce,并增加自己定制的缓存基础设施,这个基础设施比Piper早10年推出。不断扩展Google代码库是开发Piper的主要动机。
Google的源代码是公司最宝贵的资产,因此安全性是Piper设计过程中的重中之重。Piper支持文件级访问控制列表。Google代码库上的大多数内容对所有Piper用户可见,【d】不过一些重要的配置文件,或是包含商业机密的算法则被更严格地控制起来。此外,在Piper中对文件的读写访问会被记录在日志中。如果敏感数据被意外地提交到Piper,有问题的文件则会被清除。读日志允许管理人员决定在问题文件被删除之前是否让用户访问。
在Piper的工作流程(参见图4)中,开发人员会在更改文件之前在代码库中创建本地副本。这些文件存储在开发人员自己的工作空间中。Piper工作空间类似于ApacheSubversion中的工作副本、Git中的本地克隆或Perforce中的客户端一样。如所希望的,Piper代码库的更新可以被拖到工作区并与正在进行的工作合并。人们可以与其他开发人员分享工作空间的快照以便审阅。如后面描述那样,工作空间中的文件只有通过Google代码审查之后方能提交到中央代码库。
大多数开发人员通过名为Clients in the Cloud或CitC的系统来访问Piper,它是由一个云存储后端和仅使用Linux的FUSE13文件系统组成。开发者将他们的工作区看成是文件系统中的目录,包括他们在整个Piper代码库之上所做的改变。CitC支持代码浏览和标准的Unix工具,无需在本地克隆或同步状态。开发人员可以在任何地方对Piper代码库进行浏览和编辑文件,而且只有修改的文件被存储在他们的工作区中。这种架构意味着CitC工作区通常只消耗少量的存储空间(平均每个工作空间要少于10个文件),同时向开发人员呈现整个Piper代码库的无缝视图。
CitC会将所有对文件进行的写入操作储存为快照,在需要的时候,开发人员可以将文件恢复到此前的状态。快照可以被显示地命名、修复和加标签以供审查。
CitC工作区可在任何可连接到云存储系统的机器上使用,从而可以轻松地切换机器并且不中断地接收工作。它还允许开发人员在CitC工作区中相互查看彼此的工作。将所有正在进行的工作存储在云端是Google工作流程的重要组成部分。因此工作状态也可以为其他的一些工具可用,例如基于云端的构建(build)系统、自动化测试基础设施,以及代码浏览、编辑和审查工具。
一些工作流程还能够使用CitC中未提交的代码,从而让开发人员可以更高效的使用大型代码库。例如,当开发人员将代码修改发送出去以供审核时,可以启用自动提交选项,这在代码作者和审查人员身处不同时区的时候非常实用。当审查被标记为完成之后,测试就会运行;如果通过测试,代码将会被提交到代码库而无需人工干预。Google的代码浏览工具CodeSearch支持使用CitC工作区进行简单的编辑。在浏览代码库时,开发人员可以通过点击按钮进入编辑模式,进行一些简单的更改(例如修改拼写错误,或是改进一个注释)。然后,在不退出代码浏览器的情况下,他们可以启用自动提交将所做修改发送给合适的审查人员。
Piper也可以脱离CitC使用。开发人员不仅可以将Piper的工作区存储在自己的本地机器上,Piper还可以和Git有一定程度的互操作性。如今超过80%的Piper用户都在使用CitC,而且这个比例还在不断提升,这都归功于CitC给开发人员提供的诸多好处。
Piper和CitC的存在,使得在Google代码库的规模上使用单个源代码库成为可能。这些系统的设计和架构在很大程度上受到在Google中使用的、基于主干开发范型的影响,下面将详细解释。
基于主干开发
基于主干开发(Trunk-based Development)。在Piper源代码库之上,Google践行了基于主干开发(TDB)的工作方式。大量的Piper用户对“head”或者位于最近的提交进行工作,也就是称为“主干(trunk)”或“主线(mainline)”的代码的一个副本。对仓库的改变是按单一、串行的顺序进行的。基于主干开发和中央代码库一起构成了大规模代码库模型。任意一次提交之后,新的代码立刻对所有开发人员可见、可用。Piper用户工作在单一、一致的Google代码库是本文后面描述的Google代码库所提供的优点的关键之处。
基于主干开发给Google带来的好处一部分在于它避免了痛苦的合并过程,这个过程常发生在需要合并多个长生命期分支的时候。尽管分支通常被用作发布,但Google内部很少在分支上开发,而且google对这种开发方式的支持也不够好。用于发布的分支会从截自于仓库的一个特定版本。必须加入到一个发布的故障修复和增强通常都是在主干上开发的,之后通过精选(Cherry-pick)进入发布分支(见图6)。因为需要保持稳定性并限制发布波动,发布通常是“head”的一个快照,根据需要从“head”中拖出可选的一些精选项。使用具有长生命周期的分支和在分支和主线上进行并行开发是非常罕见的。
当开发新的特性时,新旧代码的路径会同时存在,通过使用条件标志(即功能开关)来控制。这种技术不仅避免了对开发分支的需要,也让通过配置更新来开启和关闭特性变得更加简单,而不是完全的二进制发布。虽然开发人员会遇到一些额外的复杂问题,但是避免了开发分支中面临的合并问题。标志翻转(Flagflips)使得用户更容易、更快地切换关闭那些有问题的新实现。这种工作方式通常被用在一些特定项目的代码中,而不是普遍的库代码,最终,标志会退役因此旧代码能够被删除。Google使用了类似的方式通过不同的代码路径来路由实时流量,以此来执行通过配置的变化来实时调优的实验。诸如A/B测试可以用于测量从代码的性能特征到与产品细微改变相关的用户参与度。
Google工作流程
Google采用几种最佳实践和支持系统以避免不破坏基于主干的开发模型。在这个开发模型中,数千名工程师每天对代码库提交数千个更改。例如,Google有一个自动化测试基础架构,它可以在每次修改被提交到代码库之后,就开始重建所有受影响的依赖文件。如果一个修改导致广泛的构建破坏,系统就会在恰当的位置上自动撤销这次改变。为了减少在一开始提交错误代码的发生率,高度可定制的Google“presubmit”基础设施在将修改加入到代码库之前会对修改进行自动测试和分析。该基础设施会对所有更改进行一系列全局的预提交分析,代码所有者可以创建自定义的分析,这些分析可以仅在用户指定的代码库目录上运行。一少部分底层核心库使用类似枝开发分支的机制,在新版本代码暴露给客户端之前,强制执行附加测试。
Google文化中一个重要的方面是鼓励开发人员控制代码质量,所有代码在进入代码库之前,都必须要经过审查。大多数开发人员可以对整个代码库任何位置的文件进行浏览并修改,除了一小部分高度机密的代码会受严格控制。让开发人员更改不熟悉的代码是有风险的,这种风险可以通过代码审查过程和代码所有权概念来缓解。Google代码库以树结构布局。每个目录都有一组所有者,他们决定是否接受对其目录中的文件进行更改。目录所有者通常是指工作在该目录下的项目开发人员。当对文件进行更改时,通常会从一个开发人员那里收到详细的代码审查,来评估更改的质量,同时还会收到来自所有者的提交批准,来评估对其代码区的更改是否合适。
代码审查员评价代码的质量,包括以下方面:设计、功能、复杂性、测试、命名、评价质量和代码风格等。Google给所有开发人员提供了一份多语言的Google代码风格指南。eGoogle开发了一个名为Critique的代码审查工具,该工具允许审查者查看代码的演变并在变更的任意行上做出评论。这鼓励开发者对代码进行更深入的修订和讨论,直到审查者认为“这样看起来很好”,才表明审查完成。
Google的静态分析系统(Tricorder【10】)和预提交基础架构提供代码质量、测试覆盖率和在Google代码审查工具中自动测试结果等方面的数据。这些计算密集的检查会定期或当代码更改被送于审查时进行。Tricorder还能够为许多错误提供建议的修复和一键点击的代码编辑。这些系统都提供重要的数据,不仅提高了代码审查的有效性,还保持了Google代码库的运转良好。
Google还有一个开发人员团队,他们会不定期的进行一系列大规模的代码清理,从而维护代码库的质量。开发人员通常将清理过程分成两个阶段执行。在这种工作方式下,首先会执行一个大的反向兼容修改。之后,进行一次规模较小的修改,移除那些已经不再引用的原始模式。一个名为Rosief 的Google工具可用于第一阶段的大规模清理和代码更改。使用Rosie,开发人员可以通过在整个存储库中执行查找和替换操作或通过更复杂的重构工具来创建大型修补程序。然后,Rosie负责将大补丁拆分成更小的补丁,独立测试它们,将它们发送给代码审查,一旦通过测试和代码审查便自动提交。Rosie按项目目录行拆分补丁,依赖前面描述代码所用者的层次结构,将补丁发送到合适的审阅者。
图7展示的是Rosie每个月所提交的修改数量。如图所示,在Google执行大规模代码变更中,Rosie扮演了非常重要的角色。使用Rosie可以平衡团队审查导致的成本。随着Rosie的流行和使用的增长,显然需要建立一些控制措施来限制Rosie对高层次的变更,这些变更将被分发给许多审阅者,而不是单一的更改或拒绝。2013年,Google采用了一个正式的大规模变更审查流程,这导致从2013年至2014年期间,通过Rosie提交的数量在减少。在评估Rosie变更时,审核委员会平衡变更的收益与代价(如花费的时间及代码库的波动带来的成本)。将来会更仔细地研究如何权衡这些关系。
总而言之,Google开发了多个方式和工具来支持他们的大规模代码库,包括基于主干开发、分布式源代码仓库Piper、工作区客户端CitC以及工作流程支持工具Critique、CodeSearch、Tricorder和Rosie等。本文将在下面的内容中讨论这个模式的利与弊。
3. 分析
本节先概述和详述了使用一个大代码库的优势和维护这种大规模模型的成本。
优势
支持Google的超大规模代码库,同时并为成千上万用户提供良好性能是一个挑战,但是Google积极推行这个模型归因于其具有的引人瞩目的优势。最重要的是,它支持:
统一的版本管理,单一的真实数据源;
广泛的代码共享和重用;
简化的依赖管理;
原子级的变更;
大规模重构;
多团队合作;
灵活的团队边界和代码所有权;
代码可视化与清晰的树结构,提供隐式团队命名空间。
单一代码库提供统一的版本控制和唯一的真实数据源。不会出现搞不清一个文件的官方版本在哪个库中被托管的情况。如果一个团队想要依赖另一个团队的代码,它可以直接依赖使用它。Google代码库包含大量有用的库,大规模存储库提供了广泛的代码共享和重用。
Google的构建系统【5】易于包含不同目录下的代码,简化了依赖管理。对一个项目的依赖关系的更改会触发对所依赖的代码的重新编译。由于所有代码在同一存储库中进行版本管理,因此只有一个真实数据源,并且不用关心依赖关系的独立版本管理。
最值得注意的是,该模型使Google避免了图8所示的“菱形依赖”(diamond dependence)问题,即A依赖于B和C,B和C都依赖于D,但B需要版本D.1而C需要版本D.2。在大多数情况下,此时编译A是不可能的。对于基本库D,要发布新版本而又不引起破坏就变得非常困难,因为其所有的调用者都必须同时更新。当库调用者托管在不同的存储库中时,更新是困难的。
在开源世界中,依赖关系通常被库更新破坏,查找所有协同工作的库版本可能是一个挑战。更新依赖的版本对于开发人员也可能是痛苦的,更新中的延迟更可能产生非常昂贵的技术债务。相比之下,使用大规模树状数据源,这对于在更新一个库时同时更新受影响的依赖关系是有意义的、并且更容易。依赖系统产生的技术债务会在进行更改时而立即交付。对基本库的更改会立即通过依赖关系链传播到依赖该库的最终产品中,而无需单独的同步或迁移步骤。
请注意,如本文所述,菱形依赖问题可能存在于源代码/ API级别、以及二进制代码之间。【12】在Google中,通过使用静态链接来避免二进制代码间的依赖问题。开发人员可以在单一连续操作中,对整个存储库中涉及数百或数千个文件进行一个大的更改。
原子级的更改能力也是大规模代码库模型的一个非常有效的特性。开发人员可以在单一连续操作中,对整个存储库中涉及数百或数千个文件进行一个大的更改。例如,开发人员可以在单次提交中重命名类或函数,却不破坏任何构建或测试。
在单个存储库或至少在一个集中式服务器上的所有源代码的可用性,使得核心库的维护者更容易在提交之前对影响面较大的更改执行测试和性能基准测试。这种方法有助于探索和测量高度破坏性变更的价值。一个具体例子是将Google数据中心转换为支持非x86机器体系结构的可行性实验评估。
使用Google大规模代码库结构,开发人员不必决定存储库的边界在哪。工程师从来不需要对共享库的开发进行分叉,或合并多个存储库以更新代码的多个复制版本。团队边界是不固定的。当为整合系统而进行项目所有权更改或规划时,所有代码都已位于同一存储库中。这种环境易于对代码库逐步重构和重组。移动一个项目并更新所有依赖关系的更改可以被自动应用于存储库,受影响的代码的开发历史会保持完整且可用。
大规模存储库的另一个贡献是由于组织成单棵树,代码库的布局易于被理解。每个团队在主树内有一个目录结构,有效地作为项目自己的命名空间。每个源文件可以唯一标识为一个字符串——一个包含修订版本号的文件路径。浏览代码库时,开发人员可以很容易地了解任何一个文件在代码库中的作用。
Google代码库还在不断演化发展。更复杂的代码库的现代化建设(例如更新到C++【11】或推出性能优化)通常由专门的代码库维护人员集中管理。这样的工作可能触及分布在数千个源代码文件中的50万个变量声明或函数调用点。由于所有项目都集中存储,专家团队可以为整个公司完成这项工作,而无需个人开发自己的工具、技术或专业知识。
这些优势可以以Google的编译器团队为例来说明,编译器团队确保Google的开发人员使用最新的工具链,并从生成的代码和“可调试性”中感受最新改进的好处。大规模存储库向团队提供在Google中完全透明地使用各种语言,并允许他们在代码库范围进行清理,以防止所做的更改破坏构建或造成问题。这大大简化了编译器验证,从而减少了编译器的发布周期,并使Google有可能安全地做定期编译器版本发布(通常每年会发布超过20个C++编译器)。
在整个Google代码库每夜构建的系统上运行性能和回归测试,所生成的数据可以让编译器团队将默认的编译器设置调整为最佳。例如,由于这种集中式的努力,Google的Java开发人员都看到他们的垃圾收集(GC)CPU消耗减少了50%以上,他们的GC暂停时间从在2014年到2015年减少了10%-40%。此外,当发现软件错误时,团队通常可能添加新的警告以防止再次发生。结合此更改,在转向新的编译器错误之前,他们扫描整个存储库以查找和修复所解决的软件问题的其他实例。拥有以前证明有问题的编译器拒绝模式是对Google的整体代码健康的重大推动。
将所有源代码存储在公共的版本控制存储库中,允许代码库维护者有效地分析和更改Google的源代码。像Refaster【11】和ClangMR【15】(通常与Rosie一起使用)等工具利用Google源代码的整体视图来执行源代码的高级转换。大规模代码库可以捕获所有依赖项信息。可以放心地删除旧API,因为可以证明所有调用者都已迁移到新的API。单一公共存储库通过确保原子级更改以及在任何时刻保证存储库的单一全局视图来大大简化这些工具。
成本和权衡
重要的是大一统的代码库绝不意味着大一统的软件设计,使用这个模型涉及一些无法避免的缺点以及权衡。
这些成本和权衡分为三类:
针对开发和执行的工具投资
代码库复杂性,包括不必要的依赖和代码发现的困难
维护代码健康所付出的努力
在许多方面,因为用于源代码的工具只有一个参考系统,所以维护单一大规模代码库只需要较简单的工具。然而,工具也必须随代码库的规模而扩展。例如,Google为Eclipse集成开发环境(IDE)编写了一个自定义插件,以便从IDE中使用大型代码库。 Google的代码索引系统支持静态分析,代码浏览工具中的交叉引用,为Emacs、Vim和其他开发环境提供丰富的IDE功能。这些工具需要持续投资,以管理不断扩大的Google代码库。
除了在构建和维护可扩展工具方面的投资,Google还必须承担运行这些系统的成本,其中的一些是高计算密集型。Google的大部分内部开发工具套件,包括自动化测试基础设施和高度可扩展的构建基础设施,这些工具对于支持大规模代码库的规模是至关重要。因此,有必要权衡应该多长时间运行这些工具一次,以平衡工具的执行成本和向开发人员提供的数据收益。
因依赖之间不存在存储库边界的交叉,运用整体模型更容易理解代码库的结构。然而,随着规模的增加,当像grep这样的标准工具陷入困境时,获取满足需求的代码可能会变得更加困难。开发者必须具备搜索代码库的能力,查找相关的库,了解其具体用法及作者。该库的开发人员也经常需要查看他们的API是如何被使用的。这需要投入大量精力到代码搜索和浏览工具上。Google已经证实这项投入是具有高回报的,提高了所有开发者的生产力,Sadowski等也详细说明了这一点【9】。
允许访问整个代码库促进了广泛的代码共享与重用。有些人会认为,这个模型依赖于Google构建系统的极端可扩展性,使其更容易添加依赖,并且削弱了软件开发人员开发更稳定更精心设计的API的动力。
在Google之外的团队,由于易于创建依赖关系,团队通常不会考虑依赖关系图,使得代码清理更容易出错。不必要的依赖会增加项目向下游构建破坏的风险,导致二进制程序的大小膨胀,并给构建和测试增加了额外工作。此外,被摒弃的项目仍保存在项目库中继续更新和维护,会造成生产力低下的问题。
Google为控制不必要的依赖做了数次尝试。一,使用已存在的工具辅助识别、去除无用的依赖或是由历史、意外等原因而链接到产品二进制文件的不必要的依赖;二,使用已存在的工具标识未充分利用的依赖,或大型库中作为重构的候选的非必要的依赖【7】。Clipper是具有上述功能的工具之一,它依靠一个自定义的Java编译器生成准确的交叉引用索引,然后使用索引构建可达性图,并确定哪些类从未使用过。该工具通过发现相对容易删除或分解的目标来指导依赖重构,具有一定效用。
纵然依赖重构和清理工具是有用的,但理想情况下,代码所有人应该首先能够防止不必要的依赖被创建。在2011年,Google开始使用API可见性的概念,默认设置新API的可见性为私有,这迫使开发者明确标记API以便其他团队使用,从Google使用大型整体库的经验中吸取到的教训是,应该尽快建立这样的机制,以促进更多整洁的依赖结构的出现。
因大部分的Google代码对所有Google开发者开放,导致一些团队养成期望其他开发者理解其代码而不会提供独立的用户文档的风气。这样的方式有利有弊,不花精力在文档写作和更新上,但有时开发者阅读超出API代码的部分,最后依赖于底层实现细节,这种行为会增加团队的维护负担,影响不想暴露给用户的特征的废弃。
此模型也要求团队在使用开源代码时相互协作。存储库保留了一部分区域用于存储开源代码(Google或外部开发)。为防止依赖冲突,如之前提到的,关键是确保在任意给定时刻开源项目只有一个版本可用。使用开源软件的团队应该不时花时间升级其代码库,以便在执行库升级时使用开源软件的新版本。
Google投入大量精力来维持代码健康度,以解决一些与代码库的复杂度和依赖管理相关的问题。例如,使用特殊工具自动检测和删除死代码,分割大型重构,自动分配代码审查(如使用Ros ie),标记弃用的API。使用这些工具并管理相应的大规模的代码更改仍然需要人工参与。团队需要审查由代码库范围的清理和集中的现代化工作产生的简单重构的持续流。
4. 替代方案
随着像Git这样的分布式版本控制系统(DVCS)的普及和使用的增长,Google已经考虑了是否从Piper迁移到Git作为其主要的版本控制系统。 Google的一个小组专注于支持Git,这是由Google主存储库之外的Android和Chrome团队使用的。由于对外合作伙伴和开源合作,使用Git对这些团队很重要。
Git社区强烈推荐开发者使用更多更小的仓库,一次Git克隆操作要求将所有内容复制到本地机器上,这对于使用大规模代码库的开发人员来说是不可能的事情。为移植到基于Git的源主机上,需要将Google仓库分成数以千计的独立仓库以获得合理的性能。这种重组需要对Google开发人员进行文化和工作流程的更改。作为比较,Google的Git托管的Android代码库被分为800多个独立的存储库。
考虑到Google已经构建的现有工具的价值以及整体代码库结构的许多优点,迁移到更多更小的存储库对于Google的主存储库来说显然是没有意义的。移动到Git或任何其他需要存储库拆分的DVCS的替代方案对于Google来说并不具有吸引力。
Google源码小组目前的投入主要集中在内部源系统持续的可靠性、可扩展性和安全性上。该团队还在与Mercurial【g】,一个开源类似于Git的分布式版本控制系统,进行合作实验。目标是给Mercurial客户端添加可扩展性,以便它可以有效地支持Google庞大的代码库。这为Google开发人员提供了一种替代方案,即将流行的DVCS风格的工作流程与中央存储库结合实验。这项工作是与开源Mercurial社区以及来自其他公司的重视整体源模型的贡献者合作的。
5. 结论
在1999年将现有的Google代码从CVS迁移到Perforce时,Google就选择整体源代码管理策略。虽然早期的Google工程师没有预料到代码库的未来规模,以及为使扩展可行而建立的所有支持工具,但他们坚持认为,单一存储库严格优于分布代码库。
这些年来,随着继续扩展集中式存储库所需的投入越来越大,Google领导层偶尔会考虑从整体模型转移是否有意义。尽管需要做出努力,但鉴于中央存储库的优势,Google反复选择坚持使用中央存储库。
源代码管理的整体模型不适合所有公司。它最适合像Google这样具有开放和协作的文化的组织,而不适用于组织中的大部分代码库是私有的或隐藏在组之间的组织。
在Google,我们发现,通过一些投入,源代码管理的整体模型可以成功扩展到一个拥有超过十亿个文件、3500万个提交和全球数千个用户的代码库。随着Google内部和外部项目的规模和复杂性不断增长,我们希望本文中描述的分析和工作流程能够帮助其他人对他们的代码库的长期结构作出决定。
致谢
本文希望感谢所有现有和前任Google开发人员基础架构团队的成员,感谢他们在构建和维护本文中提到的系统的贡献,以及帮助审查本文的许多人员;特别是:Jon Perkins和Ingo Walther,当前的Piper技术领导;Kyle Lippincott和Crutcher Dunnavant,当前和以前的CitC技术领导;Google的大规模重构大师Hyrum Wright;以及Chris Colohan、Caitlin Sadowski、Morgan Ames、Rob Siemborski,Piper和CitC开发和支持团队的深入审查意见。
感谢大连理工大学大学的王涵、李丽莎、杨燕鸣同学和东北大学的李玲、高悦同学对本次翻译所做的工作。
长按二维码发现惊喜
![](https://i-blog.csdnimg.cn/blog_migrate/8ea94b12616263ff93a81fe9e72bca0c.png)
L
O
V
E
作者简介
Rachel Potvin (rpotvin@google.com) is an engineeringmanager at Google, Mountain View,
CA.
Josh Levenberg (joshl@google.com) is a softwareengineer at Google, Mountain View, CA.
吴穹博士排版终校
引用
[1] Bloch, D. Still All on One Server: Perforce at Scale.Google White Paper, 2011
[2] Chang, F., Dean, J., Ghemawat, S., Hsieh, W.C.,Wallach, D.A., Burrows, M., Chandra, T., Fikes, A., andGruber, R.E. Bigtable: A distributed storage systemfor structured data. ACM Transactions on ComputerSystems 26, 2 (June 2008).
[3] Corbett, J.C., Dean, J., Epstein, M., Fikes, A., Frost,C., Furman, J., Ghemawat, S., Gubarev, A., Heiser,C., Hochschild, P. et al. Spanner: Google’s globallydistributed database. ACM Transactions on ComputerSystems 31, 3 (Aug. 2013).
[4] Gabriel, R.P., Northrop, L., Schmidt, D.C., and Sullivan,K. Ultra-large-scale systems. In Companion to the21st ACM SIGPLAN Symposium on Object-OrientedProgramming Systems, Languages, and Applications(Portland, OR, Oct. 22–26). ACM Press, New York,2006, 632–634.
[5] Kemper, C. Build in the Cloud: How the Build Systemworks. Google Engineering Tools blog post, 2011;
[6] Lamport, L. Paxos made simple. ACM Sigact News 32,4 (Nov. 2001), 18–25.
[7] Morgenthaler, J.D., Gridnev, M., Sauciuc, R., andBhansali, S. Searching for build debt: Experiencesmanaging technical debt at Google. In Proceedingsof the Third International Workshop on ManagingTechnical Debt (Zürich, Switzerland, June 2–9). IEEEPress Piscataway, NJ, 2012, 1–6.
[8] Ren, G., Tune, E., Moseley, T., Shi, Y., Rus, S., andHundt, R. Google-wide profiling: A continuous profilinginfrastructure for data centers. IEEE Micro 30, 4(2010), 65–79.
[9] Sadowski, C., Stolee, K., and Elbaum, S. Howdevelopers search for code: A case study. InProceedings of the 10th Joint Meeting on Foundationsof Software Engineering (Bergamo, Italy, Aug. 30–Sept. 4). ACM Press, New York, 2015, 191–201.
[10]Sadowski, C., van Gogh, J., Jaspan, C., Soederberg, E.,and Winter, C. Tricorder: Building a program analysisecosystem. In Proceedings of the 37th InternationalConference on Software Engineering, Vol. 1 (Firenze,Italy, May 16–24). IEEE Press Piscataway, NJ, 2015,598–608.
[11] Wasserman, L. Scalable, example-based refactoringswith Refaster. In Proceedings of the 2013 ACMWorkshop on Refactoring Tools (Indianapolis, IN, Oct.26–31). ACM Press, New York, 2013, 25–28.
[12] Wikipedia. Dependency hell. Accessed Jan.20, 2015
[13] Wikipedia. Filesystem in userspace.Accessed June, 4, 2015;
[14] Wikipedia. Linux kernel. Accessed Jan. 20, 2015;
[15] Wright, H.K., Jasper, D., Klimek, M., Carruth, C., andWan, Z. Large-scale automated refactoring usingClangMR. In Proceedings of the IEEE InternationalConference on Software Maintenance (Eindhoven,The Netherlands, Sept. 22–28). IEEE Press, 2013,548–551.
[20] Why We (Still) Believe In Private Offices , David Fullerton, Stack Overflow blog post, January 16th, 2015.
注释
a. Total size of uncompressed content, excluding release branches.
b. Includes only reviewed and committed code and excludes commits performed by automated systems, as well commits to release branches ,datafiles, generated files, open source files imported into the repository, and other non-source-code files.
c. Google open sourceda subset of its internal build system; see http;//www.bazel.io
d.Over 99% of files stored in Piper are visible to all full-time Google engineers.
e.https://github.com/google/styleguide
f. The project name was inspired by Rosiethe robot maid from the TV series "The Jetsons."
g.http://mercurial.selenic.com/
英文原文于RACHEL POTVIN AND JOSH LEVENBERG发表在COMMUNICATIONS OF THEACM | JULY 2016 | VOL. 59 | NO. 7,点击原文链接可以获得英文pdf版