Monorepo(Monolithic repositorie)是一种将多个项目存放在同一个代码仓库中的开发策略。传统的开发流程中,一个项目使用一个代码仓库,然而一个产品可能需要多个项目来支持,多个项目之间互相依赖,依赖关系、编译顺序、版本管理、发布顺序互相不明朗,多个项目可能存在重复的部分(例如,前后端接口定义,protobuf),由于不在一个代码仓库中,维护起来十分不方便。Monorepo 的核心观点是所有的项目在一个代码仓库中,使用一套编译脚本来处理互相之间的依赖关系,但大部分人实际仍旧在少数的几个仓库内文件夹工作。
Monorepo 优势
可见性(Visibility):每个人都可以看到其他人的代码,这样可以带来更好的协作和跨团队贡献——不同团队的开发人员都可以修复代码中的 bug,而你甚至都不知道这个 bug 的存在。
更简单的依赖关系管理(Simpler dependency management):共享依赖关系很简单,因为所有模块都托管在同一个存储库中,因此都不需要包管理器。
唯一依赖源(Single source of truth):每个依赖只有一个版本,意味着没有版本冲突,没有依赖地狱。
一致性(Consistency):当你把所有代码库放在一个地方时,执行代码质量标准和统一的风格会更容易。
共享时间线(Shared timeline):API 或共享库的变更会立即被暴露出来,迫使不同团队提前沟通合作,每个人都得努力跟上变化。
原子提交(Atomic commits):原子提交使大规模重构更容易,开发人员可以在一次提交中更新多个包或项目。
隐式 CI(Implicit CI):因为所有代码已经统一维护在一个地方,因此可以保证持续集成[3]。
统一的 CI/CD(Unified CI/CD):可以为代码库中的每个项目使用相同的 CI/CD[4]部署流程。
统一的构建流程(Unified build process):代码库中的每个应用程序可以共享一致的构建流程。
Monorepo 缺点
性能差(Bad performance):单一代码库难以扩大规模,像
git blame
这样的命令可能会不合理的花费很长时间执行,IDE 也开始变得缓慢,生产力受到影响,对每个提交测试整个 repo 变得不可行。破坏主线(Broken main/master):主线损坏会影响到在单一代码库中工作的每个人,这既可以被看作是灾难,也可以看作是保证测试既可以保持简洁又可以跟上开发的好机会。
学习曲线(Learning curve):如果代码库包含了许多紧密耦合的项目,那么新成员的学习曲线会更陡峭。
大量的数据(Large volumes of data):单一代码库每天都要处理大量的数据和提交。
所有权(Ownership):维护文件的所有权更有挑战性,因为像 Git 或 Mercurial 这样的系统没有内置的目录权限。
Code reviews:通知可能会变得非常嘈杂。例如,GitHub 有有限的通知设置,不适合大量的 pull request 和 code review。
一些知名的 Monorepo 构建工具
Bazel: 由 Google 发布,部分基于他们自己的构建系统(Blaze)。Bazel 支持多种语言,并支持大规模构建和测试
Buck: 由 Facebook 开源的快速构建系统,支持在多种语言和平台上进行不同的构建
Rush: Microsoft 用于 JavaScript 的可扩展的单一代码库管理器,能够从一个代码库构建和部署多个包
Lerna: JavaScript 的单一代码库管理器,可以与 React、Angular 或 Babel 等流行框架集成
Yarn workspace: 用一个命令在多个地方安装和更新 Node.js 的依赖项
Bit: 用于可组合软件开发的工具链
Nx: 为 monorepo 设计的小巧快捷可扩展的编译系统
Turbo: 使用 Rust 开发的前端工具链,增量的打包和构建工具
Lage: Microsoft 研发的用于提高 monorepo 构建效率的工具
ultra-runner: JavaScript 单一代码库管理脚本,支持 Yarn、pnpm 和 Lerna 插件,支持并行构建
monorepo-builder: 通过单一代码库安装和更新 PHP 包