The monorepo vs polyrepo debate generates more heat than light. Both are tools with tradeoffs — neither is universally better. Here's how I decide for each project.
The Decision Tree
Ask three questions:
- Do packages share more than 30% of their code? → Monorepo
- Do teams need fully independent CI/CD? → Polyrepo (or well-configured monorepo)
- Is your team < 20 people? → Monorepo (the coordination cost of polyrepo isn't worth it)
Monorepo with Turborepo
Turborepo is my current default. It caches build outputs intelligently — if package A hasn't changed, it skips rebuilding A and everything that depends on it. On a 10-package monorepo, this cuts CI time by 60-80%.
// turbo.json
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"test": {
"dependsOn": ["build"]
},
"lint": {}
}
}
Monorepo with Nx
Nx is more opinionated and feature-rich. Its computation cache is local + remote (Nx Cloud), its dependency graph visualization is excellent, and its generators scaffold code consistently. The cost: steeper learning curve and more "Nx way" of doing things.
I reach for Nx when the monorepo has 15+ packages and multiple languages (TypeScript + Go + Python). For pure TypeScript monorepos under 10 packages, Turborepo is simpler.
The Polyrepo Case
Polyrepos shine when:
- Packages have genuinely different lifecycles (a library updated weekly vs an app deployed hourly)
- Teams have different access control needs (open-source library vs private app)
- You need to version packages independently for external consumers
The Hidden Cost Nobody Talks About
Monorepo pain: Tooling. IDE performance degrades, git operations slow down, and CI configuration gets complex. At Google scale (billions of lines), you need custom tooling. At startup scale, this isn't a problem.
Polyrepo pain: Dependency drift. Package A depends on utils@1.2, Package B depends on utils@1.5. Now you're maintaining two versions or doing a coordinated upgrade across 8 repos. This is the silent killer of polyrepo setups.
My Default
Start with a Turborepo monorepo. Split out repos only when you hit a concrete problem that the monorepo can't solve. This path is reversible; the opposite isn't.