The Case for Monorepos
Why fewer repos is almost always the right call — and what workspaces give you that private npm registries never will.
My bias here is out in the open: more repos means more problems. More CI/CD pipelines to keep in sync, more package.json build scripts to update in multiple places, more places to find — or miss — pull requests. I am reluctant to create more repos than I need.
That doesn't mean I only have one repo. Everyone has more than one. The marketing site lives separately. The docs site lives separately. Maybe the design system got extracted to its own repo because another team said they'd use it someday. That's fine — you work with what you have. The principle I try to hold is: as many repos as you genuinely need, as few as you can get away with.
Team Autonomy ≠ Separate Repos
The argument for polyrepo is often framed as "team ownership." Different teams need to own different codebases. But I'd push back on conflating ownership with repo boundaries.
In a monorepo, teams still own their packages. The dashboard team works in packages/analytics. The platform team works in packages/ui. Nobody is in each other's files. The difference is that there's one CI pipeline running across all of them, and one build process that understands the dependencies between them.
If you're planning to do build-time composition anyway — one atomic deploy — separate repos don't give you deploy autonomy. The only thing they give you is the overhead of managing multiple repos. If you want true deploy independence, use runtime module federation. If you're doing a single build, use a monorepo.
What You Get from Workspaces
The workspace:* protocol changes the developer experience of shared code:
Before workspaces:
- Make a change to the design system
- Publish a new version to npm (or a private registry)
- Update the version in the consuming app
- Run CI on the consuming app
- Discover a bug, repeat from step 1
With workspaces:
- Make a change to the design system
- The consuming app picks it up immediately — they're linked locally
No token rotation on private npm registries. No waiting for packages to propagate. No version bump PRs just to test a change. The packages are just directories with a package.json.
The Type Safety Argument
This one genuinely convinced me. If I'm maintaining a design system in a separate repo and I change the props on a Button component, my CI passes. The consuming apps are on the published version; they haven't gotten the change yet. I ship. They break in production.
In a monorepo with shared packages, changing Button's props immediately affects every consumer in the same repo. TypeScript surfaces every breakage. CI catches it before anything ships.
That's the design system governance problem solved structurally. You can't accidentally break consumers you can't see. The entire dependency graph is visible and verifiable in one build.
What Workspaces Don't Solve
The honest part: workspaces solve the code sharing and resolution problem. They don't solve:
- Build ordering — what needs to compile before what
- Selective execution — only running tests for what changed
- Remote caching — sharing build artifacts across machines
These are the problems Turborepo exists for. Workspaces are the foundation. Turborepo is what makes the monorepo fast enough to be worth it.
pnpm's Catalog Feature
One practical addition worth knowing: pnpm's catalog lets you declare a single version for dependencies across the entire workspace.
# pnpm-workspace.yaml
catalog:
react: "^19.0.0"
typescript: "^5.8.0"Then in any package:
{ "dependencies": { "react": "catalog:" } }Instead of 8 packages each declaring their own React version (which drifts), one entry in the catalog keeps everything aligned. If you're not using pnpm, you can replicate the pattern — a shared JSON or YAML file of canonical versions, enforced by a CI script — but the pnpm catalog makes it native.
The underlying problem it solves is real regardless of tool: in distributed codebases, dependency versions drift. Keeping them aligned requires either tooling or discipline. Tooling wins.
Keep reading
Enjoyed this? Get more like it.
Deep dives on system design, React, web development, and personal finance — straight to your inbox. Free, always.