单一存储库:请不要

来自译者:您好,Habr! 是的,这是另一篇有关单一存储库优缺点的文章。 我打算写一篇有关如何使用单一存储库,如何从Maven转换为Bazel以及它产生了什么的文章。 但是,当我考虑这个问题时,Lyft的开发人员发表了一篇很棒的文章,我决定为您翻译。 我保证会发表文章的补充内容,以及将bazel作为续集的经验。
我们正处于新的2019年,我将再次讨论将组织的所有源代码存储在“ Monorepository”中的优势(或缺乏优势)。 对于那些不熟悉这种方法的人,其想法是将所有源代码存储在版本控制系统的单个存储库中。 当然,另一种选择是将源代码存储在几个独立的存储库中,通常将它们沿服务/应用程序/库的边界划分。

在本文中,我将这种方法称为“多存储库”。

一些IT巨头使用单一存储库,包括Google,Facebook,Twitter等。 当然,如果这样的信誉良好的公司使用单一存储库,那么这种方法的好处应该是巨大的,我们都应该这样做,对吗? 不行 正如文章标题所言:“请不要使用单一存储库!” 怎么了 因为在很大程度上,单一存储库将解决多存储库也解决的所有相同问题,但同时会激发您代码的强大一致性,并需要付出巨大的努力来提高版本控制系统的可伸缩性

因此,从中期和长期来看,单一存储库不会提供任何组织优势,而会使公司的最佳工程师患有创伤后综合症(表现为口水和关于git性能的不连贯的喃喃自语)。


简短的题外话:“大规模”是什么意思? 这个问题没有单一答案,而是因为 我敢肯定,你会问我这个问题,假设大约有100个开发人员在编写全职代码。

单一存储库的理论优势以及为什么没有用于多资源库(或错误)的工具无法实现它们的原因


理论优势1:更轻松的协作和代码共享


单一存储库的支持者声称,当所有代码都在同一个存储库中时,重复代码的可能性较小,并且不同的团队更有可能在共同的基础架构上一起工作。

这是即使是中型单一存储库的痛苦事实(在本节中将不断听起来):对于开发人员而言,将所有存储库代码保留在其工作站上或使用grep之类的实用程序搜索整个代码库很快变得不切实际。 因此,任何想要扩展的单一存储库都应提供以下两点:

1)类似虚拟文件系统的东西,它允许您仅在本地存储部分代码。 这可以通过使用专有的文件系统(例如Perforce)来实现,该文件系统可以使用Google内部的G3工具或Microsoft的GVFS本地支持此模式。

2)复杂的工具即服务(即服务),用于索引/搜索/查看源代码。 因为 没有开发人员打算将所有源代码都以可搜索的状态存储在其工作站上,因此能够在整个代码库中进行这种搜索至关重要。

基于开发人员在任何给定时间只能访问一小部分源代码这一事实,下载部分单一存储库或下载几个独立存储库之间至少有一些区别吗? 没有区别

在建立索引/搜索/浏览和类似代码的上下文中,这种假设的工具可以轻松地搜索多个存储库并组合结果。 实际上,这正是GitHub上的搜索以及Sourcegraph等更复杂的搜索和索引工具的工作方式

因此,从大规模的代码协作工作的角度来看,在任何情况下,开发人员都被迫仅使用部分代码库并使用更高级别的工具。 代码是存储在单个存储库中还是存储在几个独立的存储库中都没有区别,以相同的方式解决问题,并且代码协同工作有效性仅取决于工程文化,而不取决于源代码的存储方式

理论上的优势2:一个程序集/无依赖项管理


下一个论点,通常由单一存储库的支持者引用,是将所有代码存储在一个单一存储库中,从而使您无需管理依赖项,因为 同时收集所有代码。 这是骗人的! 大规模地,每次有人向版本控制系统提交更改时(或者更重要的是,在创建新的分支或拉取请求时,更重要的是在CI服务器上更频繁地),根本无法重建所有源代码并运行所有自动化测试。 为了解决此问题,所有大型单一存储库都使用其复杂的构建系统(例如Google的Bazel / Blaze或Facebook的Buck ),该系统旨在监视更改及其依赖块并构建源代码的依赖图。 此图使您可以组织有效的装配结果和测试缓存,因此仅更改及其相关性才需要重新装配和测试。

而且,由于 收集的代码最终应被部署,并且,您知道,所有软件都不能一次部署,因此,重要的是控制所有程序集工件,以便在必要时重新进行工件重做。 从本质上讲,这意味着即使在单一存储库的世界中,自然也可以同时存在多个版本的代码,并且必须对其进行仔细的监视和协调。

支持单一存储库的人还会认为,即使考虑到跟踪程序集/依赖项的需求,这仍然提供了不可否认的优势,因为 一次提交描述了整个世界的完整状态。 我要说的是,鉴于依赖关系图已经存在,因此这种优势颇具争议,并且将每个独立存储库的提交标识符作为该图的一部分包含在内似乎是一项微不足道的任务,实际上,Bazel可以轻松地与多个独立存储库以及一个独立存储库一起使用单一存储库,从开发人员中抽象出基础级别。 而且,很容易实现这样的自动重构工具,该工具可以一次自动更新几个独立存储库中的依赖库的版本,从而拉平了该部分中单存储库和多存储库之间的差异(稍后会详细介绍)。

最终结果是,对于单一存储库和多存储库,大规模组装/部署的现实在很大程度上是相同的。 工具没有区别,开发人员编写代码也不应区别

理论上的优势3:代码重构是一个简单的原子提交


最后,单一存储库的支持者提到的最后一个优点是,由于易于搜索,一个存储库使代码重构更加简单,并且一次提交可以覆盖整个存储库的想法。 这是不正确的,原因如下:

1)如上所述,开发人员将无法在其本地计算机上编辑或搜索整个代码库。 因此,任何人都可以轻松地克隆自己的整个存储库并进行grep / replace的想法很难付诸实践。

2)即使我们假设开发人员可以在复杂的虚拟文件系统的帮助下克隆并编辑整个代码库,那么这种情况会发生多久? 我并不是说要修复共享库的实现中的错误,因为在单个存储库和多存储库的情况下,这种情况将得到同等处理(假定如上所述,构建/部署系统类似)。 我说的是更改库API,在调用该库的地方将出现许多编译错误。 在非常大的代码库中, 几乎不可能对基本API进行更改,在合并冲突迫使您再次开始该过程之前,所有相关团队将对其进行预览 。 开发人员有2种真正的可能性:他可以放弃并提出解决API问题的方法(实际上,这种情况比我们所有人都希望的发生得更多),或者他可以偏离现有的API,编写新的API,然后长期从事在整个代码库中痛苦地更新对旧API的所有调用。 在任何情况下, 这都是与polyrepository完全相同的过程

3)在面向服务的世界中,应用程序由许多松散耦合的组件组成,这些组件使用某种类型的描述明确的API相互交互。 大型组织迟早会改用IDL(接口描述语言),例如Thrift或Protobuf,它们允许您进行类型安全的API并进行向后兼容的更改。 如前面有关组装/部署的部分所述, 不能同时部署代码 。 它可以在一段时间内部署:几小时,几天甚至几个月。 因此,开发人员必须考虑其更改的向后兼容性。 这是现代软件开发的现实,许多人想忽略但不能忽略。 因此,关于服务(与API库相反),开发人员应使用上述两种方法之一(不要更改API或不经历弃用周期),并且对于单一存储库和多存储库来说这是绝对相同的

说到大型代码库重构,许多大型组织正在开发自己的自动化重构工具,例如Facebook最近发布的fastmod 。 与往常一样,此工具可以轻松地使用一个或多个独立的存储库。 Lyft拥有一个称为“重构器”的工具。 它的工作方式类似于fastmod,但是它可以自动在我们的多个存储库中进行更改,包括创建提取请求,跟踪评论状态等。

单一存储库的独特缺点


在上一节中,我列出了单一存储库提供的所有理论优势,并指出,要利用这些优势,需要创建难以置信的复杂工具,该工具与多存储库的工具没有什么不同。 在本节中,我将提到单一存储库的两个独特缺点。

缺点1:强大的连接能力和开源软件


从组织上讲,单一存储库激发了紧密耦合且脆弱的软件的创建。 它给开发人员一种感觉,他们可以轻松地纠正抽象中的错误,尽管实际上,由于在整个代码库中尝试立即进行更改时出现的不稳定的组装/部署过程以及人/组织/文化因素,他们不能这样做。

多重存储库中的代码结构表示团队/项目/抽象/代码所有者之间的清晰透明边界,并迫使开发人员仔细考虑交互界面。 这是一个微妙的但非常重要的优势:它使开发人员可以更广泛地考虑和长期发展。 而且,使用多存储库并不意味着开发人员不能超出存储库的范围。 是否发生这种情况,仅取决于开发文化,而不取决于使用的是单存储库还是多存储库。

强绑定对于其源代码的开放也具有严重的后果。 如果公司要创建或使用开源软件,则必须使用多个存储库。 当公司尝试从其单一存储库中以开放源代码布局项目时(源代码的导入/导出,公共/私有错误跟踪器,抽象层来提取标准库中的差异等)时发生的失真不会导致有效的协作,并且建立社区以及增加大量开销。

缺陷2:版本控制系统的可伸缩性



为数百名开发人员,数亿行代码和大量提交扩展规模控制系统是一项艰巨的任务。 5年前创建的Twitter单一存储库(基于git)是我职业生涯中最看不起的项目之一。 运行像git status这样的简单命令花费了几分钟 。 如果存储库的本地副本太旧,则更新可能要花费几个小时 (当时甚至习惯将具有存储库副本的硬盘驱动器发送给具有最新版本代码的远程员工)。 我记得这不是在嘲笑Twitter开发人员,而是为了说明这个问题有多复杂。 我可以说,五年后,Twitter单一存储库的性能仍然与Tilling团队的开发人员希望看到的性能相去甚远,这并不是因为他们努力了。

当然,在过去的五年中,该领域已经取得了一些进展。 用来开发Windows的Microsoft的Git VFS导致了git的真实虚拟文件系统的出现,我在上面将其描述为扩展版本控制系统的先决条件(随着购买Microsoft Github,似乎可以找到这种扩展级别了) GiHub提供给企业客户的功能中的应用程序)。 当然,Google和Facebook继续在其内部系统中投入大量资源,以便它们继续运行,尽管其中几乎没有一个公开可用。

那么,如前一部分所述,为什么要求工具包与多存储库完全相同,为什么您通常需要通过扩展版本控制系统来解决这些问题? 没有合理的理由。

结论


正如软件开发中经常发生的情况一样,我们以最成功的软件公司为例,并尝试借鉴其最佳实践,而不了解导致这些公司成功的确切原因。 我认为单一存储库就是这种情况的典型示例。 Google,Facebook和Twitter在其代码存储系统中投入了大量资源,只是想出一种解决方案,该解决方案与多存储库所需的解决方案基本相同,但是却产生了强大的联系,并且需要在扩展版本控制方面进行大量投资

实际上,在很大程度上,公司是如何与代码,协作,牢固绑定等一起工作的。 直接取决于工程文化和领导力,与使用单一存储库还是使用多元存储库无关 。 两种解决方案对开发人员而言都是相同的。 那么为什么要使用单一存储库? 请不要!

Source: https://habr.com/ru/post/zh-CN435306/


All Articles