Google and Meta have run internal monorepos for more than ten years, with a build-infrastructure team that few mid-size companies replicate. Turborepo, Nx, and pnpm workspaces brought the practice within reach of teams of ten. The payoff shows up when the monorepo solves a problem the team has today.
Three problems justify the switch: code that more than one project imports, an API change that has to cross packages in the same deploy, and CI that runs everything from scratch on every push.
What a monorepo solves
Importing code without publishing
The design system, shared utilities, shared types, and lint configuration live in a workspace package and enter through a direct import. The team stops coordinating published versions across repositories.
// in any workspace package
import { Button } from '@company/ui'
import { formatDate } from '@company/utils'
import type { User } from '@company/types'
Refactors that cross projects
A shared function changed signature? You update the function and its consumers in the same commit. The monorepo removes the window where two repositories run different versions of the same API. The book Software Engineering at Google describes how the company runs this model at its scale, build system included.
CI that only pays for the diff
Turborepo and Nx read the dependency graph and run build and test only on the packages the diff touches. The Turborepo caching guide documents the per-package hash key and remote cache, which carries the gain into cloud CI.
# build and test only on packages the diff touched
turbo run build test --filter='...[HEAD^1]'
What a monorepo does not solve
A monorepo does not fix bad code. Two projects with conflicting architectures stay conflicting in the same directory tree, and the conflict shows up in the same pull request. A monorepo also does not replace team conventions. Without shared rules for lint, formatting, and folder layout, you create coupling between teams that disagree.
When to skip the monorepo
- The team has one or two projects and no code shared between them.
- Each project deploys on its own cycle and the domains do not overlap.
- No one on the team has run Nx or Turborepo before, and the quarter does not have room for the learning curve.
- The product changes direction every week; each project's velocity outweighs consistency across them.
Turborepo or Nx
Turborepo
A JavaScript and TypeScript team that wants caching and incremental pipelines with little configuration. The configuration reference fits in a few dozen lines, and the Vercel integration is native.
// turbo.json
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "dist/**"]
},
"test": { "dependsOn": ["build"] },
"dev": { "cache": false, "persistent": true }
}
}
Nx
A larger monorepo, more than one language, or a team that wants code generation, graph visualization, and framework plugins. The Nx mental model explains per-operation caching and input-hash invalidation. More power, more configuration to maintain.
# generates a React application in the monorepo
nx generate @nx/react:application my-app
# opens the dependency graph in the browser
nx graph
The structure I check in review
my-monorepo/
├── apps/
│ ├── web/ # Next.js
│ ├── mobile/ # React Native
│ └── api/ # Node.js
├── packages/
│ ├── ui/ # design system
│ ├── config/ # shared ESLint, TypeScript
│ ├── utils/
│ └── types/
├── turbo.json
├── pnpm-workspace.yaml
└── package.json
The rule I apply in review: apps/ holds what deploys, packages/ holds what gets imported. A package never imports from an app. An app may import from any package. The pnpm workspaces documentation shows the pnpm-workspace.yaml that ties the tree together.
The decision
A monorepo belongs in the plan once you have, or are one quarter away from having, multiple projects that share code, teams that work across more than one project, and changes that have to break an API between packages in the same deploy. A monorepo stays out when none of those three shows up in the last three months of retrospectives.
Adopting a monorepo because a large company runs one matters little for your situation: their model includes a build-infrastructure team you do not have. The decision belongs to today's problem. Polyrepo serves most teams well until that problem shows up.
Enjoyed this content?
I build web products and AI solutions the right way — solid architecture, maintainable code, and real delivery.
Let's talk