2021年的一个周五下午,我在工位上盯着CI/CD的进度条,手心全是汗。
当时我们维护着12个独立的Git仓库(Polyrepo模式),前端业务组件库发了一个小版本的Bug Fix。理论上这只是个补丁,但因为三个业务线项目依赖的组件版本不一致——有的锁死了旧版本,有的用了^前缀自动更新,结果上线后,核心的支付弹窗在A项目中正常,在B项目中直接报错白屏。
为了修复这个问题,我们不得不排查所有仓库的package.json,逐个发版、打包、部署。原本10分钟的修复,硬是折腾到了凌晨两点。
那一刻我发誓:一定要把这些代码合到一个仓库里去。
但当我真正带着团队转向Monorepo(单体仓库)半年后,我才意识到:解决了一个旧地狱,往往意味着开启了一个新地狱。
如果你也在犹豫是该拆还是该合,希望我这段"反复横跳"的血泪史,能给你一些真实的参考。
这种"各自为政"的痛,你也经历过吗?
在Polyrepo模式下,最大的痛点不是代码分散,而是**“上下文丢失”**带来的协作熵增。
回到2021年那个阶段,我们的团队结构是典型的"竖井式":3个前端小组,分别维护PC端、H5端和管理后台。后端则是微服务架构,拆分了8个服务仓库。
这种看起来"解耦"的架构,实际上带来了隐形成本:
- 代码复用变成了通过网络发包: 修改一行公共工具代码,需要经历
修改 -> Commit -> Publish NPM -> 各业务项目 npm install -> 测试的漫长链路。 - 版本依赖地狱: 就像开头的案例,项目A用了
utils v1.0,项目B用了utils v2.0,当两者在浏览器运行时共享某个全局状态时,诡异的Bug就诞生了。
这就是Polyrepo的原罪:它在物理上隔离了代码,也在逻辑上隔离了团队的认知。 开发者变得只扫门前雪,没人关心公共基建的演进。
盲目跟风Monorepo,基建不仅没变好,反而崩了
带着对"Google全公司几十亿行代码都在一个仓"的憧憬,我们在2022年初启动了迁移。
我们简单粗暴地把所有前端项目移动到了一个Git仓库下,用lerna做管理。起初的蜜月期确实很爽:
- 原子化提交:改公共库和改业务代码可以在一个Commit里完成;
- 依赖统一:大家终于都用同一个版本的React和Lodash了。
但仅仅过了三个月,噩梦开始了。
数据不会说谎: 我们的CI(持续集成)构建时间,从原本单项目平均5分钟,暴涨到了40分钟。
因为Monorepo如果不配合高阶的构建工具,每次提交代码,CI都会傻傻地把仓库里所有项目重新跑一遍测试、构建一遍镜像。
更可怕的是开发体验的恶化。
那时候新招了一个实习生,光是git clone代码下来就花半小时,IDE打开项目光建立索引就卡死。我记得很清楚,有次周会上,后端组长直接拍桌子:“我现在改个文案都要等半小时流水线,这到底是进步还是退步?”
我的反思: Monorepo 不是 把代码放在一个文件夹里就完事了。它本质上是一套**“重型工程化体系”**。如果没有增量构建、远程缓存(Remote Caching)、细粒度权限控制这"三板斧",中小团队上Monorepo就是自掘坟墓。
适合中小团队的"中间路线":工具链+工作区
经过那次40分钟构建时间的教训,我们没有退回Polyrepo,而是引入了更现代的工具链进行改造。
现在的架构,我称之为**“务实的Monorepo”**。我们没有追求Google那种极致的单体,而是采用了Turborepo + pnpm workspace 的组合。
1. 引入增量构建,只做该做的事
我们调整了CI脚本,利用Turborepo的依赖拓扑分析能力。
具体操作:
当开发者修改了packages/ui-lib时,CI只会构建依赖这个库的APP A和APP B,而完全无关的APP C会被直接跳过(或使用缓存)。
// turbo.json 简略配置
{
"pipeline": {
"build": {
"dependsOn": ["^build"], // 依赖上游构建
"outputs": ["dist/**", ".next/**"]
},
"test": {
"dependsOn": ["build"], // 依赖当前项目的构建
"inputs": ["src/**/*.tsx", "test/**/*.ts"]
}
}
}
结果: 在缓存命中的情况下,我们的平均构建时间从40分钟骤降回了3-5分钟。这直接挽救了团队的幸福感。
2. 代码边界与Code Owner机制
为了防止"大锅饭"导致代码质量失控(比如谁都去改两行核心库的代码),我在Gitlab上配置了严格的CODEOWNERS规则。
- 公共库(libs/): 必须由架构组核心成员Review通过才能合并;
- 业务项目(apps/): 由对应的业务组长Review。
这既保留了"谁都能看代码"的透明度,又保证了"核心逻辑不被随意篡改"的安全感。
到底怎么选?给PM和Tech Lead的建议
回顾这两年的折腾,关于技术选型,我总结出一条核心逻辑:架构必须服务于组织结构(康威定律),而不是反过来。
如果你的团队正面临选择,不妨对照以下标准:
建议坚守 Polyrepo (多仓库) 的情况:
- 团队之间完全隔离,业务几乎无交集(外包性质强)。
- 没有专职的基建/DevOps人员维护构建工具。
- 对权限极度敏感(A项目代码绝对不能被B项目组看到)。
建议尝试 Monorepo (单仓库) 的情况:
- 多个项目之间共享大量的UI组件、工具函数、TypeScript类型定义。
- 前端/全栈团队人数在10-50人之间,沟通成本开始显著上升。
- 关键点: 有人愿意花时间去配置Nx、Turborepo或Rush等现代化工具,而不是仅仅靠
cp命令。
总结与行动
没有完美的技术架构,只有最适合当下的权衡。
对于大多数中小团队(尤其是SaaS类、中台类产品),我强烈推荐**“基于Workspace的轻量级Monorepo”**方案。它不需要你像Google那样自研构建系统,只需利用现有的Node.js生态工具,就能获得80%的收益。
如果你决定迈出这一步,请从这3件小事做起:
- 资产盘点: 统计目前有多少重复代码散落在各个仓库中(尤其是Utils类和Type定义),用数据量化重构价值。
- 工具先行: 不要手动搞,直接试用
pnpm workspace或Turborepo的官方脚手架初始化一个Demo,把两个关联性最强的项目放进去跑通流程。 - 制定规矩: 在合并之前,先定好目录结构规范(如
apps/存放应用,packages/存放库),否则三个月后你的仓库会变成一个巨大的垃圾场。
最后,做个小调查:
在你的日常开发中,你是更享受 Monorepo 带来的"全局掌控感"(A),还是更喜欢 Polyrepo 的"互不打扰"(B)?
欢迎在评论区告诉我你的选择和理由,也许你的痛点就是我下一篇文章的主题。