单一存储库:请,必须

单一存储库:请,必须

为课程学生准备的文章翻译 “DevOps 实践和工具” 在 OTUS 教育项目中。

您应该选择单一存储库,因为它在您的团队中促进的行为是透明度和共同责任,尤其是随着团队的成长。无论哪种方式,您都必须投资于工具,但如果默认行为是您在命令中想要的行为,那总是更好。

我们为什么要谈论这个?

马特·克莱因撰写了这篇文章 “Monorepos:请不要!”  (译者注:哈布雷翻译 “单一储存库:请不要”)。我喜欢马特,我认为他非常聪明,你应该阅读他的观点。他最初在 Twitter 上发布了这项民意调查:

单一存储库:请,必须

翻译:
今年元旦,我将讨论单一存储库是多么荒谬。 2019年悄然开始。本着这一精神,我向您提供一项调查。谁是大狂热分子?支持者:
- 莫诺雷波
-
- 民意调查不正确/两者皆有

我的回答是:“我确实是这两个人。”我们不讨论 Rust 如何成为一种药物,而是看看为什么我认为他对单一存储库的看法是错误的。简单介绍一下你自己。我是 Chef Software 的 CTO。我们拥有大约 100 名工程师、大约 11-12 年的代码库和 4 个主要产品。其中一些代码位于多存储库(我的起始位置)中,一些位于单一存储库(我当前的位置)中。

在开始之前:我在这里提出的每个论点都适用于这两种存储库。在我看来,没有任何技术原因说明您应该选择一种类型的存储库而不是另一种类型。您可以使任何方法发挥作用。我很高兴谈论这个问题,但我对人为的技术原因不感兴趣,为什么一个人比另一个人优越。

我同意马特观点的第一部分:

因为在规模上,单一存储库将解决多存储库解决的所有相同问题,但同时迫使您紧密耦合代码,并需要付出难以置信的努力来提高版本控制系统的可扩展性。

无论您选择单一存储库还是多存储库,您都必须解决相同的问题。您如何发布版本?您的更新方法是什么?向后兼容?跨项目依赖?什么样的建筑风格是可以接受的?您如何管理构建和测试基础架构?这个清单是无穷无尽的。随着你的成长,你会解决所有这些问题。没有免费的奶酪。

我认为马特的观点与我尊敬的许多工程师(和经理)的观点相似。这是从处理该组件的工程师或处理该组件的团队的角度来看的。你会听到这样的话:

  • 代码库很大——我不需要这些垃圾。
  • 测试更困难,因为我必须测试所有这些我不需要的垃圾。
  • 处理外部依赖项更加困难。
  • 我需要我自己的虚拟版本控制系统。

当然,所有这些观点都有道理。这两种情况都会发生 - 在多存储库中,除了构建所需的垃圾之外,我还有自己的垃圾......我可能还需要其他垃圾。所以我“简单地”创建了检查整个项目的工具。或者我创建一个带有子模块的假单一存储库。我们可以在这周围走一整天。但我认为马特的论点忽略了主要原因,我非常支持单一存储库:

它引发沟通并显示问题

当我们分离存储库时,我们实际上造成了协调和透明度问题。这对应于我们对团队的思考方式(尤其是个体成员对团队的思考方式):我们对某个组成部分负责。我们的工作相对孤立。我的团队和我们正在开发的组件的边界是固定的。

随着架构变得越来越复杂,一个团队不再能够单独管理它。很少有工程师脑子里有整个系统。假设您管理一个由团队 B、C 和 D 使用的共享组件 A。团队 A 正在重构、改进 API,并更改内部实现。因此,这些更改不向后兼容。您有什么建议?

  • 查找所有使用旧 API 的地方。
  • 新API有哪些地方不能使用?
  • 您可以修复并测试其他组件以确保它们不会损坏吗?
  • 这些团队现在可以测试您的更改吗?

请注意,这些问题与存储库类型无关。您需要找到 B、C 和 D 团队。您需要与他们交谈,找出时间,了解他们的优先事项。至少我们希望你会。

没有人真正愿意这样做。这比修复该死的 API 没那么有趣。这都是人性的,而且很混乱。在多存储库中,您可以简单地进行更改,将其交给处理该组件(可能不是 B、C 或 D)的人员进行审查,然后继续。 B、C 和 D 队暂时可以保留当前版本。当他们认识到你的天才时,他们就会焕然一新!

在单一存储库中,默认情况下会转移责任。 A 团队更改了组件,如果不小心,就会立即破坏 B、C 和 D。这导致 B、C 和 D 出现在 A 的门口,想知道 A 团队为何破坏组件。这告诉 A 他们不能跳过我上面的列表。他们必须谈论他们要做什么。 B、C、D 可以移动吗?如果 B 和 C 可以,但 D 与旧算法行为的副作用密切相关怎么办?

然后我们必须谈谈如何摆脱这种情况:

  1. 支持多个内部 API,并将旧算法标记为已弃用,直到 D 可以停止使用它。
  2. 支持多个发布版本,一种使用旧界面,一种使用新界面。
  3. 延迟发布 A 的更改,直到 B、C 和 D 可以同时接受它。

假设我们选择了 1 个 API。在本例中,我们有两段代码。旧的和新的。在某些情况下相当方便。我们重新检查旧代码,将其标记为已弃用,并与 D 团队就删除时间表达成一致,对于多存储库和单存储库来说基本相同。

要发布多个版本,我们需要一个分支。现在我们有两个组件 - A1 和 A2。 B 队和 C 队使用 A2,D 队使用 A1。我们需要每个组件都做好发布准备,因为在 D 继续前进之前可能需要安全更新和其他错误修复。在多存储库中,我们可以将其隐藏在感觉良好的长期分支中。在单一存储库中,我们强制在新模块中创建代码。 D 队仍需对“旧”组件进行更改。每个人都可以看到我们在这里付出的成本 - 我们现在的代码数量是原来的两倍,并且适用于 A1 和 A2 的任何错误修复都必须适用于它们两者。通过多存储库中的分支方法,这隐藏在cherry-pick后面。我们认为成本较低,因为没有重复。从实际角度来看,成本是相同的:您将构建、发布和维护两个基本相同的代码库,直到您可以删除其中一个。不同之处在于,对于单一存储库,这种痛苦是直接且可见的。 这更糟,但那很好。

最后,我们到了第三点。释放延迟。 A 所做的改变可能会改善 A 团队的生活。重要,但并不紧急。我们可以推迟一下吗?在多存储库中,我们推动它来固定工件。当然,我们会将这一情况告诉 Team D。请继续使用旧版本,直到赶上!这会让你扮演胆小鬼的角色。 A 团队继续开发他们的组件,忽略了 D 团队正在使用越来越过时的版本这一事实(这是 D 团队的问题,他们很愚蠢)。与此同时,D 团队对 A 团队对代码稳定性的粗心态度(如果他们真的谈论过的话)评价不佳。几个月过去了。最后,D 队决定考虑更新的可能性,但 A 队只有更多的改动。 A队几乎不记得他们何时或如何打破了D。升级更加痛苦并且需要更长的时间。这会将其进一步发送至优先级堆栈。直到有一天,我们在 A 遇到安全问题,迫使我们建立一个分支。 A 团队必须回到过去,找到 D 稳定的点,修复那里的问题,并做好发布的准备。 这是人们事实上做出的选择,也是迄今为止最糟糕的选择。 只要我们能够互相忽略,对 A 队和 D 队来说似乎都是好事。

在单一存储库中,第三种确实不是一个选择。您被迫以两种方式之一处理这种情况。您需要了解拥有两个发布分支的成本。了解如何保护自己免受破坏向后兼容性的更新的影响。但最重要的是: 你无法避免进行一场艰难的谈话。

根据我的经验,当团队规模变大时,就不再可能记住整个系统,而这是最重要的部分。您必须提高系统中不和谐的可见性。您必须积极努力,让团队将目光从他们的组件上移开,关注其他团队和消费者的工作。

是的,您可以创建尝试解决多存储库问题的工具。但我在大型企业中教授持续交付和自动化的经验告诉我:不使用其他工具的默认行为就是您期望看到的行为。 多存储库的默认行为是隔离,这就是重点。单一存储库的默认行为是共同责任和透明度,这就是重点。 在这两种情况下,我将创建一个工具来平滑粗糙的边缘。作为领导者,我每次都会选择单一存储库,因为工具需要强化我想要的文化,而文化来自微小的决策和团队的日常工作。

只有注册用户才能参与调查。 登录拜托

谁是最大的狂热分子?支持者:

  • 莫诺雷波

  • 民意调查不正确/两者皆有

33 位用户投票。 13 名用户弃权。

来源: habr.com

添加评论