27、Monorepo与多仓库策略:Monorepo的优劣、Google/微软的Monorepo实践、多仓库的依赖管理
说到代码仓库的组织方式,这其实是个挺有意思的话题。我见过不少团队,一开始随便建个仓库就开干,等项目大了才发现处处是坑。今天咱们就来聊聊 Monorepo 和多仓库这两种策略,看看它们各自适合什么场景。
什么是 Monorepo?
Monorepo 的全称是 Monolithic Repository,说白了就是把所有相关项目的代码都放在同一个仓库里。你想想看,一个仓库里既有前端代码、后端代码,还有公共库、配置文件、文档,甚至 CI/CD 脚本——这就是 Monorepo 的典型形态。
我最早接触 Monorepo 是在一家中型互联网公司。当时我们有个平台项目,前端三个团队、后端两个团队,代码分散在五个仓库里。每次跨团队协作,光是拉代码就要拉五个仓库,别提多痛苦了。
Monorepo 的优势
先说说 Monorepo 的好处,我总结了几点:
- 代码可见性高:所有代码都在一个地方,谁改了什么都一目了然。我在项目中遇到过,某个后端接口改了签名,前端团队完全不知道,结果上线后直接 500。如果是 Monorepo,这种问题很容易在 Code Review 阶段被发现。
- 原子性提交:一次提交可以同时修改前端、后端和公共库。这听起来简单,但实际用起来非常爽。你不需要担心「先合并 A 仓库还是先合并 B 仓库」这种问题。
- 统一的构建和部署:所有项目共享一套 CI/CD 流水线,不用为每个仓库单独配置。我建议小团队优先考虑 Monorepo,因为维护成本真的低很多。
- 依赖管理简单:公共库的版本号只有一个,不存在「A 仓库依赖 v1.0,B 仓库依赖 v2.0」这种尴尬局面。
Monorepo 的劣势
当然,Monorepo 也不是银弹。它有几个明显的短板:
- 仓库体积巨大:Google 的 Monorepo 有几十 TB,普通公司根本扛不住。我记得有个朋友的公司,仓库拉下来要半小时,每次 git status 都卡半天。
- 权限控制困难:所有代码都在一个仓库里,你很难做到「前端团队只能看前端代码」。虽然可以用 CODEOWNERS 文件做限制,但总归不如多仓库那么干净。
- 工具链要求高:普通的 Git 根本处理不了超大仓库,需要专门的工具,比如 Google 的 Piper、微软的 GVFS。这些工具的学习成本可不低。
核心观点:Monorepo 适合项目间耦合度高、团队规模不大的场景。如果你的项目之间几乎没有依赖关系,那多仓库可能更合适。
Google 和微软的 Monorepo 实践
说到 Monorepo,就不得不提 Google 和微软。这两家公司的实践可以说是教科书级别的。
Google 的 Monorepo:Google 几乎把所有代码都放在一个仓库里,包括 Chrome、Android、Google Search 等。他们自己开发了 Piper 系统来管理这个巨型仓库。Piper 支持分布式存储、细粒度权限控制,还能做到毫秒级的文件访问。我曾在 Google 的分享会上听过一个细节:他们的 CI 系统每天要跑几百万次测试,但依然能保持分钟级的反馈速度。嗯,这背后是巨大的基础设施投入。
微软的 Monorepo:微软的 Windows 团队也是 Monorepo 的忠实用户。Windows 的代码仓库有 300 多万个文件,总大小超过 300GB。为了解决 Git 的性能问题,微软开发了 GVFS(Git Virtual File System)。GVFS 的核心思想是「按需下载」——你只拉取你需要的文件,而不是整个仓库。我在项目中用过 GVFS,说实话,体验还不错,至少拉代码的速度能接受。
个人经验:如果你也想搞 Monorepo,建议先评估一下团队规模和基础设施。小团队用 Git + LFS 就够了,大团队才需要考虑 Piper 或 GVFS 这种级别的工具。
多仓库策略
多仓库策略,顾名思义,就是每个项目都有自己的仓库。这种方式的优点很明显:
- 仓库体积小:每个仓库只包含自己的代码,拉取速度快。
- 权限控制精细:每个仓库可以独立设置访问权限,谁可以读、谁可以写,清清楚楚。
- 技术栈自由:每个团队可以自由选择技术栈,不用跟其他团队保持一致。
但多仓库也有它的痛点,最典型的就是依赖管理。
多仓库的依赖管理
多仓库环境下,依赖管理是个大问题。我见过最典型的场景是这样的:
- 团队 A 开发了一个公共库 lib-x,版本号 v1.0。
- 团队 B 和团队 C 都依赖 lib-x。
- 团队 A 升级了 lib-x 到 v2.0,但团队 B 没跟上。
- 结果团队 C 升级后,发现和团队 B 的代码不兼容了。
为什么会这样?因为多仓库环境下,每个仓库的依赖版本是独立管理的。要解决这个问题,通常有几种做法:
| 方案 | 说明 | 优缺点 |
|---|---|---|
| 语义化版本 | 严格遵循 semver 规范,主版本号变化表示不兼容 | 依赖开发者自觉,容易出错 |
| Monorepo 化 | 把公共库和项目放在同一个仓库 | 解决了版本问题,但仓库会变大 |
| 包管理工具 | 使用 npm、Maven、NuGet 等工具统一管理 | 需要搭建私有源,维护成本高 |
| CI/CD 联动 | 公共库发布新版本时,自动触发下游项目的构建 | 实现复杂,但效果最好 |
我个人比较推荐「语义化版本 + CI/CD 联动」的组合。我曾经在一个项目中,用 Jenkins 的 Pipeline 实现了自动触发:公共库发布新版本后,自动创建下游项目的 PR,并在 PR 中更新依赖版本。这样既保证了版本一致性,又不会强制升级。
避坑指南:我曾经见过一个团队,为了省事,把所有公共库的版本都写成 latest。结果某天一个公共库发布了破坏性更新,所有项目全部挂掉。嗯,从那以后我再也不敢用 latest 了。
Monorepo vs 多仓库:怎么选?
其实没有绝对的好坏,关键看你的场景。我画了一张图,帮你快速决策:
从这张图可以看出,决策的关键在于两个因素:项目间的耦合度和团队规模。耦合度高、团队小,Monorepo 是首选;耦合度低、团队大,多仓库更合适;如果介于两者之间,可以考虑混合策略——核心项目用 Monorepo,外围项目用多仓库。
我的建议
最后说说我的个人看法。如果你是一个初创团队,或者项目规模不大,我建议你直接上 Monorepo。原因很简单:省事。你不需要操心依赖版本、跨仓库协作、CI/CD 配置这些破事,可以把精力集中在业务上。
但如果你在大型公司,或者项目已经发展到几百人的规模,那多仓库可能是更现实的选择。不过要注意,多仓库不是简单的「每个项目一个仓库」,而是要配套完善的依赖管理和 CI/CD 体系。否则,你会陷入「依赖地狱」——相信我,那滋味不好受。
一句话总结:Monorepo 适合「小而美」,多仓库适合「大而全」。选哪个不重要,重要的是你的团队能驾驭它。
公众号:蓝海资料掘金营,微信deep3321