先说结论:没有银弹,只有取舍
Monorepo(单仓库)和 Multirepo(多仓库)不是「先进 vs 落后」的关系,而是三种成本之间的权衡:依赖管理成本、CI 成本、协作成本。选错方向,团队会在其中某一项上持续付出代价。
一、依赖管理
Multirepo 的天然优势是版本边界清晰:每个包独立发版,通过版本号(如 npm 的语义化版本、Maven 的 GAV 坐标)声明依赖。代价是「跨仓库改动」很痛苦——改一个底层库,需要发布新版本,再逐个仓库升级依赖、跑测试、合并。依赖图越深,升级链路越长。
Monorepo 把依赖变成仓库内的路径引用(如 npm/pnpm/yarn 的 workspace、Go 的 module replace、Bazel 的 target 依赖)。改一处,所有使用者立刻可见,不需要发版。代价是:
- 需要工具支持「只构建受影响的包」,否则每次 CI 都全量构建;
- 依赖必须显式声明,否则容易出现「幽灵依赖」(代码能跑但 package.json 里没写);
- 工具链选择变少,往往要引入 Nx、Turborepo、Bazel、Pants 这类构建编排系统。
二、CI 时间
这是最容易被低估的一项。
Multirepo 的 CI 是「按仓库隔离」的:改哪个仓库跑哪个仓库,流水线简单,缓存天然有效。
Monorepo 的 CI 如果不做优化,会随仓库增长线性甚至超线性变慢。可行的优化手段包括:
- 受影响分析:基于文件变更和依赖图,只跑受影响的包(Nx、Turborepo、Bazel 都提供这类能力)。
- 远程缓存:把构建产物按内容哈希缓存到远端,命中即跳过。
- 任务并行与分片:把测试集拆到多台机器。
- 合并队列(merge queue):保证主干始终绿,避免「排队等 CI」变成新瓶颈。
判断标准很简单:如果团队没有精力维护构建系统,Monorepo 的 CI 会迅速变成拖累。
三、团队协作
Monorepo 让代码可见性最大化,跨团队改动一次提交即可完成,代码评审可以覆盖全部影响面。但它对工程纪律要求更高:
- 需要 CODEOWNERS 之类的机制明确各目录负责人;
- 需要统一的代码风格、lint、提交规范;
- 仓库权限粒度粗,敏感代码难以隔离。
Multirepo 则相反:权限、节奏、技术栈都能独立,团队自治度高。代价是重复建设、依赖漂移、跨仓库重构困难。
四、怎么选
可以按这几个问题自检:
- 组件之间是否频繁互相改动? 频繁 → 倾向 Monorepo。
- 是否有专人/专门团队维护构建与 CI? 没有 → 倾向 Multirepo,或先用「少量大仓库」过渡。
- 是否需要严格的权限与合规隔离? 需要 → 倾向 Multirepo。
- 仓库规模是否已经让 clone、IDE 索引变慢? 是 → 考虑部分拆分或使用稀疏检出(sparse checkout)。
实践中的常见折中:按业务域划分少数几个 Monorepo,而不是全公司一个大仓库,也不是每个服务一个仓库。
小结
Monorepo 用「构建系统的复杂度」换「依赖与协作的顺畅」,Multirepo 用「跨仓库协调成本」换「隔离与自治」。决策依据不是潮流,而是团队当前最痛的那一项成本。