是否应该转向单代码库?开发者无废话指南
单代码库是炒作还是真正的工程价值?
单代码库(Monorepo)受到的关注并不令人意外。Meta、谷歌、Shopify和优步都在使用它们。 impressive,对吧?但请注意,仅仅因为大公司在使用,并不意味着它适合我们所有人。像所有其他架构决策一样,尝试采用单代码库既有好处也有陷阱。
在本文中,我将带您了解:
- 为什么工程团队选择单代码库
- 采用单一代码库的优缺点
- 采用这种模式的公司案例研究和优缺点分析
- 关于是否转向单一代码库架构适合您的用例的指南
让我们一步步解构一切。
什么是多代码库,为什么它是默认选择?
这是一个提醒
多代码库(Polyrepo,是“多态存储库”的缩写,也称为多个存储库)是指每个服务、每个应用程序或组件都有自己的 Git 存储库。
这听起来有吸引力吗?当然是,尤其是在过去。团队可以快速移动,拥有自己的存储库,并选择他们想要的任何技术栈——我曾经工作过的公司在某个时候都利用了这一点。
但哪里会出问题?
这就是多代码库在扩展时可能会惩罚你的地方:
- 重复:维护一个共享的库存储库:将其存储在不同的存储库中并更新它变成了一个邪恶的头痛问题。
- 安全风险:修补漏洞怎么样?将其迁移到 10 个不同的版本并不容易。开销
- 工具混乱:所有工具都是重复的。为每个存储库创建新的 CI 管道,以及新的 linting 配置、测试工具等。
这些正是 Slack 工程师在尝试将更新传播到数十个存储库时遇到的问题。这导致他们在多个地方重复同样的努力,耗费了他们宝贵的时间,并导致不一致的体验。
您有多少次遇到在多个不同位置修复同一个错误的讽刺情况?
那么,什么是单代码库?
单代码库并不是一个将所有代码都倾倒进去的大文件夹。它是一种逻辑上有目的的方法,用于跨多个团队或项目统一单个代码库,同时允许模块化。
以谷歌为例
他们开发了世界上最大的单代码库。而且它有效。因为他们构建了:
- Bazel:一个智能且严格的依赖控制缓存构建系统
- Hermetic builds:每个构建都是密封的,意味着它可以重现和隔离。
- 自定义 CI 工具:它针对有效的大规模集成进行了调整。
这不是混乱,这是有纪律的工程。
而且不仅仅是谷歌
- Airbnb:使用 Yarn Workspaces + Lerna 用于共享的前端包。
- Shopify:将店面和管理工具统一在一个存储库中,以简化更改。
- Stripe:集中了他们的内部库和测试逻辑。
看到趋势了吗?单代码库不是关于集中化——它们是关于简化。
为什么这些团队选择单代码库
让我们讨论一下您可以通过单代码库捕获的实际好处,尤其是在正确执行时。
1. 跨团队协作变得更容易
现在您不必与多个团队协调来更新共享类型或修复设计错误。一个 PR。一个管道。完成。
在 Shopify 的案例中,这意味着开发人员可以一次性更新管理、店面、合作伙伴仪表板和合作伙伴仪表板中的计费逻辑。原子提交 = 更少的错误。
2. 一致的工具
想象一下,在所有项目中以完全相同的设置启动 CI 管道。ESLint 配置?共享。测试工具?共享。基础设施即代码?是的,那也是。
3. 测试和代码可见性改进
想要运行跨服务集成测试吗?在单代码库中,模拟真实世界流程要容易得多——因为一切都在那里。
这导致 Stripe 在核心库上拥有更紧密的反馈循环和更好的测试覆盖率。
4. 标准化和入职
新开发人员加入?他们只需要克隆一个存储库,他们就能得到一切。不再是“去寻找正确的存储库”。
你不能忽视的挑战
然而,这并不全是合并的拉取请求和阳光。单代码库也有自己的一系列问题。
1. 构建时间可能爆炸式增长
如果没有像 Bazel 或 Nx 这样的智能构建工具,您的 CI 管道将会非常慢。优步的工程师亲眼目睹了这一点,直到他们大量投资于缓存和仅受影响的构建。
2. Git 性能在规模上受到影响
即使在谷歌的规模上,一个 git status
命令也是一个非平凡的操作。自定义工具实际上是使 Git 功能化所必需的。
对于较小的团队,这可能还不是问题,但随着扩展,请记住这一点。
3. 文化转变是真实的
总会有单一转变模式和结构。拥有自己存储库的团队将不可避免地抵制整合的需要。您必须同步编码约定、发布时间线和审查策略。
更大的问题是您的团队有多愿意紧密协作。
何时应该使用单代码库?
根据经验,当团队频繁接触共享代码或设计系统时,考虑单代码库可能是有意义的。
好的候选者
- 前端繁重的组织,如 Airbnb、LinkedIn
- 在集成产品团队工作的员工
- 构建跨平台 SDK 或库的公司
不好的候选者
- 具有严格自治团队结构的微服务繁重组织(想想亚马逊)
- 完全不同的技术栈,如 Node.js + Go + Python
- 需要严格隔离服务的受严格监管的行业
退后一步,问问自己:
- 您的团队多久更新一次共享代码?
- 多个存储库中是否有相同库的副本?
- 目前,是否存在入职问题?
如果您对以上三个问题都回答是,那么您很可能在不知不觉中处于单代码库的痛苦场景中。
构建成功的单代码库
仅仅采用单代码库设计不会产生结果;在没有防护措施的情况下采用是无效的。
使用工具如
- Nx 或 Turborepo 用于帮助构建速度和缓存
- Lerna 用于版本化包
- Yarn Workspaces 或 pnpm 用于依赖管理和链接
- Bazel 用于超大规模构建
设置边界
- 强制执行模块所有权 - 不允许跨团队随机编辑
- 设置作用域 linting 和测试规则
- 使用 depcruise 或 madge 进行依赖监控
这不是控制问题。这是一个协作问题,用户可以安全且自信地自由交互。
常见问题解答:您问,我们答
问:单代码库不就是一个大单体吗? 不完全是。单体是一个难以更改的应用程序。单代码库仅仅是一个沙盒,可以细分为微服务、库、应用程序等,所有这些都可以独立运行但共处一地。
问:我的构建时间不会变得无法忍受吗? 只有如果您不投入资源优化您的 CI/CD 管道、缓存和任务自动化(Nx 和 Bazel)。
问:如果只有部分团队想要单代码库怎么办? 可以混合使用。一些组将常用的前端库拉入单代码库,而后端服务保持多代码库。没有规则指导这一点。
最后思考:构建适合您组织的东西
在组织结构中,可定制性胜过统一性。单代码库提供了临床记录的优势,但前提是您准备对其进行大量投资。在没有必要工具和协作文化的情况下,多代码库是完全可接受的(有时是首选)。
专注于您现在试图解决的挑战,而不是当前趋势是什么,并用它来指导您的架构选择。
也就是说,问问自己:
我的团队以何种方式更快地执行,具有更多的自主权或一致性?
这就是您定义存储库结构所需的答案。