Should You Actually Use a Monorepo? A Decision Tree, Not a Manifesto
Every few months someone on a team I'm working with proposes migrating to a monorepo, and every few months I watch the conversation split into two camps that both feel a little too certain of themselves. One camp treats monorepos as an obviously superior default, citing Google and the ability to atomically change a shared package and all its consumers in one commit. The other treats them as an anti-pattern that inevitably collapses under CI times and merge contention. I've now lived through both a genuinely painful multi-repo setup and a monorepo migration that solved real problems, and my honest take is that the right answer depends entirely on specifics that neither camp usually bothers to ask about.
Start with the actual pain, not the architecture
The question I've learned to ask first isn't "should we use a monorepo," it's "what specific thing is currently costing us time." If the answer is "coordinating a change across a shared UI library and three consumer apps requires three separate PRs, three separate version bumps, and a day of waiting for each app to pick up the new package version," that's a real, specific, monorepo-shaped problem. If the answer is "our repos feel disorganized" or "I read that Google does this," that's not a problem a monorepo actually solves, and you'll spend real engineering time solving a problem you didn't have.
I went through this firsthand with a shared component library that three separate Next.js apps consumed as a private npm package. Every change to a shared button component meant: PR against the library repo, review, merge, publish a new version, then three more PRs bumping the dependency in each app, then waiting for each app's CI to confirm nothing broke. A one-line CSS fix could realistically take half a day to actually reach production across all three apps. That's the kind of concrete, measurable friction that justifies the migration cost.
The decision tree, roughly
If your team is a single small group — say, under eight or ten engineers — working on genuinely separate products that don't share code, a monorepo buys you very little and costs you real setup complexity. Keep separate repos. The coordination overhead a monorepo solves doesn't exist yet, because there's nothing to coordinate.
If you have multiple packages or apps that share code and change together frequently — a design system, shared types, shared API clients consumed by a web app and a mobile app — that's the strongest signal for a monorepo, regardless of team size. The shared-code-changes-together pattern is really the core justification; everything else is secondary.
If your team is large (multiple dozens of engineers) but working on genuinely independent services with almost no shared code — think separate backend microservices each owned by a different team with different deploy cadences — a monorepo can actually hurt you unless you invest seriously in tooling for scoped CI, code ownership boundaries, and build caching. Companies like Google and Meta run enormous monorepos successfully, but they've built entire internal platforms to make that tractable; a team without that investment can end up with CI pipelines that run every check on every PR because nobody configured the scoping correctly.
If you're not sure whether you're in the "shares code" camp, a decent litmus test is to look at your last twenty pull requests across your existing repos and count how many required a corresponding, coordinated change in another repo. If it's a meaningful fraction, you have a real signal, not a vibe.
Once you're in, picking the tool
Assuming a monorepo actually makes sense, the tooling decision comes down to how much you need beyond basic workspace linking.
pnpm workspaces alone is the right starting point for most small-to-medium monorepos, and I mean that as more than a compromise — it might be genuinely sufficient forever. It handles the core problem, symlinking local packages together and hoisting shared dependencies, with almost no configuration:
pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
// apps/web/package.json
{
"dependencies": {
"@myorg/ui": "workspace:*"
}
}
That workspace:* protocol is doing the heavy lifting — it links directly to the local package on disk instead of fetching from a registry, and pnpm rewrites it to a real version number automatically when you publish. If your whole monorepo problem is "I want changes to propagate without a publish step," this alone might fully solve it, and I've seen teams over-engineer past this point when they didn't need to.
Turborepo is what I reach for once the pain shifts from "linking packages" to "CI takes too long because we rebuild and retest everything on every change." It sits on top of pnpm (or npm/yarn) workspaces and adds a task graph with caching, so a change to apps/web doesn't force a rebuild of apps/mobile if there's no dependency between them.
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/", "dist/"]
},
"lint": {
"outputs": []
},
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"]
}
}
}
The dependsOn: ["^build"] syntax means "build this package's dependencies first," and Turborepo's caching means that if nothing in a package or its dependencies changed since the last successful build, it just replays the cached output instead of rerunning anything — locally, and remotely if you wire up remote caching through Vercel or a self-hosted cache. On a monorepo with a handful of apps and a dozen shared packages, this is routinely the difference between an eight-minute CI run and a ninety-second one for a small, isolated change.
Nx is the heavier option, and it earns that weight when you specifically need things Turborepo doesn't try to do: a dependency graph visualizer your team actually uses to reason about impact, generators and scaffolding conventions enforced across a large org, or you're specifically in a framework ecosystem — Angular in particular — where Nx has very deep, opinionated integration. I've used Nx on a team of around forty engineers where the nx graph command genuinely got used in planning conversations to answer "if we change this package, what's the blast radius," and that visibility was worth the extra configuration overhead. On smaller teams I've found the same investment mostly sits unused, which is really the core tradeoff: Nx's power scales with org size and the number of people who need shared conventions enforced automatically rather than through code review discipline.
Lerna, for what it's worth, I'd mostly leave out of a decision made in 2026. It was the standard for JS monorepo publishing workflows for years, and it's not dead — it's actually been folded under Nx's stewardship and gained some of Nx's caching — but for new projects there's little reason to reach for it over pnpm workspaces plus Turborepo or Nx directly, unless you specifically need its versioning and changelog automation and don't want to adopt something like Changesets separately.
What I'd actually tell a team starting today
Default to pnpm workspaces with no additional tooling until you feel actual CI pain, not hypothetical pain. When CI time becomes the bottleneck, add Turborepo — it's a genuinely small lift on top of an existing pnpm workspace setup, mostly just the turbo.json above and swapping your CI script to call turbo run build instead of looping over packages by hand. Only reach for Nx if you have a specific org-scale need — enforced generators, a large multi-team org, deep framework-specific tooling — that you can name concretely, not because a monorepo tooling comparison blog post ranked it "most powerful." And regardless of which tool you land on, the actual monorepo decision itself should trace back to a specific, current pain you can point to in your last month of pull requests, not an architectural preference borrowed from a company operating at a completely different scale than yours.
Written by AIDToolStack Editorial
The AIDToolStack editorial team reviews developer tools, AI products, and MCP servers with hands-on testing and honest assessments. We use the tools we review every day in production.
References & Further Reading
Related Posts
Sponsor Our Newsletter
Reach thousands of developers who are actively evaluating AI tools, MCP servers, and dev infrastructure. Our weekly newsletter goes to engaged technical decision-makers.
All sponsored content is clearly labeled per our editorial policy.