[翻译]不要使用 Mono Repo!

原文链接:https://medium.com/@mattklein123/monorepos-please-dont-e9a279be011b

本来准备自己写一篇,结果发现了一篇和我考虑的点一模一样的文章,所以就顺势翻译了一下。

在 2019 年开头,我正好在讨论将一个组织中的所有代码放在 monorepo 中的优缺点。对于那些不熟悉 monorepo 概念的人,我简单的介绍一下:mono repo 的主旨就是将所有代码存储在同一个版本控制(VCS)代码库中。当然,另一种选择是拆分方法是把服务、应用、库的代码存储在不同的版本控制库中。出于本文的目的考虑,我会将多个代码库的解决方案称为「polyrepo」。

包括 Google、Facebook、Twitter 在内的许多巨头公司都在使用 monorepo。既然这么多巨头公司都在使用 monorepo,monorepo 的收益肯定超级大,那么我们也应该使用 monorepo 对吗?不!如标题上所说:请不要使用 monorepo!为什么?因为从规模上来讲,monorepo 必须要解决 polyrepo 需要解决的每一个问题,同时又不利于低耦合,还需要付出巨大的代价来解决 VCS 的可伸缩性。

因此,从中长期来看,monorepo 带来的组织效益为 0,同时不可避免的让组织中的一些工程师陷入糟糕案例 PSTD(表现为流口水以及对 git 内部性能感到不满)。

简要说明:怎么定义「大规模」?这个问题没有确切的答案,但是我知道我肯定会被问到,所以为了讨论,先定义「大规模」意味着超过 100 个开发人员进行全职开发。

理论的 monorepo 收益以及为什么他们不可以在没有 polyrepo 风格的工具的情况下实现(一个谎言)

理论上的好处 1:更轻松的协作和代码共享

Monorepo的支持者会争辩说,当所有代码都存在于单个存储库中时,代码重复的可能性很小,而且不同的团队更有可能在共享的基础架构上合作。

这里有一个惨淡的现实:即使是一个中等大小的 monorepo(这将是在本节中反复出现的一个主题):任何开发人员想要将整个库都放在本地,或者使用 grep 之类的工具进行搜索都会变得不好用。因此,任何希望扩展的monorepo必须提供两件事:

  1. 某种类型的虚拟文件系统(VFS),允许部分代码在本地显示。这可以通过专用的 VCS 来实现(比如 Perforce),他可以通过像 Google G3 内部工具 或者微软的 GVFS 来自然的运作。
  2. 完善的源代码索引/搜索/发现功能即服务。由于没有任何一个开发人员将所有代码控制在可搜索的状态,因此对整个代码库进行检索的能力是至关重要的。

假设开发人员一次只能访问一小部分代码库,那么通过 VFS 检出树的一部分还是检出多个代码仓库有什么本质的区别吗?没有。

就源代码的索引、搜索和发现功能来说,一个工具可以轻松的迭代许多代码库并整理结果。实际上,这就是 GitHub 自身的搜索功能以及更新和更复杂的索引和协作工具(例如 Sourcegraph)的工作原理。

因此,就协作和代码共享而言,开发人员将通过更高层的工具来接触代码的各个子部分。代码是在 monorepo 还是在 polyrepo 中无关紧要;解决的问题是相同的,协作和代码共享的功效与工程文化息息相关,而与代码存储无关。

理论上的好处 2:单一构建、无依赖管理

支持者通常会说的下一件事情是:将所有代码都放在一个存储库中,因此不需要依赖管理,因为所有源代码都是同时构建的。

这当然是谎话。

在大规模项目中,根本没有办法简单在每次提交中(或者,更重要、更频繁触发 CI) rebuild 整个代码库以及运行所有自动化测试。

为了解决这个问题,所有大型monorepos都开发了复杂的构建系统(例如,参见 Google 的Bazel/Blaze 和 Facebook 的 Buck ),其设计方式是内部跟踪依赖关系并构建有向无环图(DAG)。该 DAG 允许高效的构建和测试缓存,因此仅需要构建和测试更改的代码或依赖于此的代码。

此外,由于构建的代码必须实际被部署,而不是所有软件在同一时间被部署,因此必须仔细跟踪构建,以便可以根据需要将先前部署的软件重新部署到新主机,这一点至关重要。这种现实意味着,即使在 monorepo 的世界,多版本同时存在不受控的代码必须仔细跟踪和协调。

Monorepo 的支持者会争辩说,即使需要大量的构建/依赖项跟踪,仍然有很大的好处,因为单个 commit / SHA 可以描述整个世界。而我认为这种好处是存疑的。对已有的 DAG,增加各独立仓库的 SHA 作为 DAG 的一部分只是个微不足道的提升,实际上,Bazel可以在跨存储库或在单个存储库中无缝工作,从而让开发人员从底层布局中抽出。

此外,可以轻而易举地构建自动重构工具,该工具可以在许多存储库中自动增加依赖库的版本,从而模糊了该区域中的 monorepo 和 polyrepo 之间的差异(下文有更多介绍)。

最终结果是,无论使用 monorepo 还是 polyrepo ,大规模构建/部署管理的现实都基本相同。这些工具无关紧要,开发人员也不必编写代码。

理论上的好处 3:代码重构很容易/原子提交

monorepo支持者通常会吹嘘的最后一个好处是,当所有代码都放在一个存储库中时,由于易于搜索以及单个原子提交可以覆盖整个代码库的思想,它使代码重构变得更加容易。

这是一个谬误,有多种原因:

  1. 如上所述,开发人员将无法大规模地在其本地计算机上编辑或搜索整个代码库。因此,可以克隆所有代码并简单地执行 grep / replace 的想法在实践中并不简单。
  2. 如果我们假设开发人员可以通过复杂的 VFS 克隆并编辑整个代码库,那么下一个问题是它实际发生的频率是多少?我并不是说要修复共享库实现中的错误,因为无论使用 monorepo 还是 polyrepo(假定与上一节中所述的构建/部署工具类似),这种修复方式都是相同的。我说的是对库API的更改,该更改会对其他代码产生后续的构建中断影响。在非常大的代码库中,可能无法更改底层 API 并让每个受影响的团队对其进行代码审查,然后再合并冲突让流程重新开始。开发人员面临两个现实的选择。首先,他们可以放弃并解决 API 问题(这种情况发生的频率比我们想承认的要高)。其次,他们可以弃用现有的 API,实现新的 API,然后经历整个代码库中逐个弃用变更的繁琐过程。无论哪种方式,这都是在 polyrepo 中进行的完全相同的过程。
  3. 在面向服务的世界中,应用程序现在由许多松散耦合的服务组成,这些服务使用某种类型明确指定的 API 相互调用。大型组织不可避免地会迁移到IDL,例如 Thrift 或 Protobuf,以允许类型安全的 API 和向后兼容的更改。如上一节有关构建/部署管理的描述,代码不会同时部署。它可能会部署数小时,数天或数月。因此,现代开发人员必须考虑不受控的向后兼容性。这是现代应用程序开发的一个简单现实,许多开发人员希望忽略但不能忽略。因此,在服务方面,相对于库API,开发人员必须使用上述两个选项之一(放弃更改 API 或经历弃用周期),无论使用 monorepo 还是 polyrepo,这都没什么不同。

就实际跨大型代码库进行重构更改而言,许多组织最终开发了自动化重构工具,例如 Facebook最近发布的 fastmod。与其他工具一样,这样的工具可以在单个存储库中或跨多个存储库的地方轻松运行。Lyft 有一个内部称为“重构器”的工具,可以执行此操作。它的工作方式类似于 fastmod,但可以在我们的仓库中自动进行更改,包括打开 PR,跟踪评论状态等。

monorepo 独有的缺点

在上一节中,我列出了monorepo提供的所有理论收益,并解释了为什么说要实现这些收益,必须开发与 polyrepo 所需的工具无异的极其复杂的工具。在本节中,我将介绍 monorepos 的两个独特缺点。

缺点 1:紧密耦合和 OSS

从组织上讲,monorepo鼓励紧密结合和开发脆弱的软件。它让开发人员以为自己轻松地解决抽象错误,尽管本质上由于交错的构建/部署和要求开发人员在整个代码库中进行更改所固有的人/组织/文化因素的现实,开发人员实际上无法在现实世界中解决实际问题。

Polyrepo 代码布局提供了清晰的团队/项目/抽象/所有权边界,并鼓励开发人员仔细考虑合同。这是一个微妙而又非常重要的好处:它使组织的开发人员拥有了更具可扩展性和长期性的思维方式。此外,使用 polyrepo 并不意味着开发人员无法跨越存储库边界。无论这种情况是否发生,都取决于当地的工程文化,而不是组织使用的是 monorepo 还是 polyrepo。

紧密耦合对开源也有实质性的影响。如果组织希望创建或轻松使用OSS,则需要使用 polyrepo。大型 monorepo 组织所承担的控制成本(反向导入/导出,私人/公共问题跟踪,垫片层以提取标准库差异等)不利于有效的 OSS 合作和社区建设,并且也给内部的工程师带来了大量组织上的开销。

缺点 2:VCS 可扩展性

Twitter

15884259612192.jpg

将单个VCS扩展到数百个开发人员,数亿行代码,还要保证快速的提交速度是一项艰巨的任务。Twitter 大约 5 年前推出的 monorepo(基于 git)是我职业生涯中目睹的最大的软件工程狂潮之一。运行简单的命令(如 git status)将花费几分钟。如果单个克隆的记录差的太多,则要花上几个小时才能赶上(有时甚至有一种做法是从最近的克隆开始将硬盘驱动器运送给远程员工)。我提出这个问题并不是专门为了取笑 Twitter 的工程化,而是为了说明这个问题有多难。有人告诉我,五年后,Twitter 的 monorepo 的性能仍然不是那里的开发人员工具团队所希望的,并且不是因为缺乏尝试导致的。

当然,过去 5 年在这方面也取得了进展。微软的 git VFS(用于内部开发 Windows)已经解决了为 git 创建真正的 VFS 的问题,如上所述,这是对 monorepo 可扩展性的要求(随着 Microsoft 对 GitHub 的收购,这种 git 可扩展性解决方案也能在 GitHub 的企业产品中有迹可循)。当然,尽管这些工作都没有公开可用,但 Google 和 Facebook 继续在内部系统上投入大量资源以保持其运行。

但是,既然如上节所述,在所有时候都需要解决 VCS 可伸缩性问题的问题,也需要构建与polyrepo 所需的工具相同的工具,为什么还要用 monorepo 呢?没有充分的理由。

总结

与软件工程中的情况一样,我们倾向于从技术最成功的公司那里寻求最佳实践的指导,而没有理解使这些公司大规模成功的巨大工程量。我认为 Monorepos 是一个极好的例子。Google,Facebook和 Twitter 已在其代码存储系统上进行了大量投资,最终只是获得了与使用 polyrepo 时所需要的解决方案相同的解决方案,但导致了紧密耦合,并且需要对 VCS 可伸缩性进行大量投资。

坦率的现实是,从规模上讲,组织在代码共享,协作,紧密耦合等方面的表现是工程文化和领导才能的直接结果,与使用单一仓库或多仓库无关。面对这种情况,为什么要优先使用 monorepo?请不要!

植入部分

如果您觉得文章不错,可以通过赞助支持我。

如果您不希望打赏,也可以通过关闭广告屏蔽插件的形式帮助网站运作。

标签: git, 翻译

仅有一条评论

  1. christian

    已经入坑...

添加新评论