Codebase organisation comparison
Monorepo vs polyrepo
One repository for everything, or one repository per project? The answer drives how teams coordinate, how CI scales, and how easy cross-project changes feel.
TL;DR
- Monorepo — one Git repository contains many projects. Atomic cross-project commits, shared tooling, single version of truth — at the cost of CI infrastructure investment.
- Polyrepo — each project owns its repo. Smaller, focused, simpler — at the cost of dependency drift and harder cross-cutting changes.
- The right choice tracks team coupling. Tightly-coupled teams → monorepo. Loosely-coupled, independent teams → polyrepo.
Side-by-side comparison
| Aspect | Monorepo | Polyrepo |
|---|---|---|
| Repositories | One | Many |
| Cross-project change | One PR atomic across services | Multiple PRs coordinated; release ordering matters |
| Dependency versioning | One version of everything | Each repo pins its own versions — drift inevitable |
| Code sharing | Trivial — import directly | Via published packages with semver |
| CI scope | Must compute affected projects to stay fast | Naturally scoped per repo |
| Tooling | Nx, Turborepo, Bazel, Pants required at scale | Standard Git + per-repo CI is enough |
| Discovery | One search across everything | Per-repo search; cross-repo discovery is hard |
| Access control | Path-based or trust-based | Per-repo, GitHub/GitLab native |
| Cloning | Slow as repo grows (shallow / sparse helps) | Fast — only what you need |
| Used by | Google, Meta, Microsoft, Uber, X | Most open-source, most startups |
Layout side-by-side
Monorepo
acme/ ← single repo
apps/
web/
mobile/
admin/
services/
auth/
billing/
orders/
packages/
ui/ ← shared UI lib
config/
types/
package.json ← root workspace
turbo.json ← build orchestration
.github/workflows/ Polyrepo
acme/web ← separate repo
acme/mobile ← separate repo
acme/admin ← separate repo
acme/auth-service ← separate repo
acme/billing-service ← separate repo
acme/orders-service ← separate repo
acme/ui-library ← published as @acme/ui
acme/types ← published as @acme/types
# Each repo:
# package.json with deps on @acme/ui@^1.2
# its own CI, releases, README When to choose a monorepo
- Shared design system / types. Frontend, mobile-web, and admin tools sharing a UI library — atomic upgrades are huge.
- Tightly coupled services. Updating an API contract and all its consumers should be one reviewable change.
- Standardised tooling. One linter config, one CI template, one dependency upgrade strategy across many projects.
- Single-org refactoring. "Rename this function everywhere" should be one PR, not 23.
- You have engineers to invest in CI tooling. Nx/Turborepo/Bazel are not free to set up and maintain.
When to choose polyrepos
- Independent teams with independent products. No reason to couple their release cycles.
- Open-source projects. External contributors expect focused, well-scoped repos.
- Strong service boundaries already. No frequent cross-service changes — coordination overhead is rare.
- Limited platform team. No one to own monorepo tooling; standard GitHub workflow is fine.
- Compliance separation. Some code must be visible only to specific teams; per-repo ACLs are simpler than path-based.
English phrases engineers use
Monorepo conversations
- "This PR updates the shared types and all consumers in one change."
- "CI is rebuilding everything — we need affected-project detection."
- "Codeowners for the auth package — review required from the auth team."
- "Use workspaces for the package linking, not symlinks."
- "Sparse checkout — junior devs don't need the entire codebase."
Polyrepo conversations
- "We need to release the library first, then bump it in the consumer."
- "This is dependency drift — three services on three different SDK versions."
- "The change spans four PRs across four repos — let's coordinate the merge order."
- "Renovate bot is keeping all repos on the latest minor."
- "Open a cross-repo issue — link them so we don't lose track."
Quick decision tree
- Frontend + design system shared across products → Monorepo
- Microservices with frequent cross-service API changes → Monorepo (with tooling)
- Open-source library → Polyrepo
- Small team, single product, < 10 engineers → Either works; default monorepo for simplicity
- Independent product teams, weekly cross-team contact → Polyrepo
- Compliance-isolated codebase (PCI, classified) → Polyrepo
- No platform team to maintain tooling → Polyrepo
- You said "monorepo" and your team groaned → Polyrepo — culture matters
Frequently asked questions
What is a monorepo?
A monorepo is a single version-control repository containing the code for many projects, services, or libraries — owned by the same organisation. Google, Meta, and Microsoft famously run multi-billion-line monorepos; smaller teams use them for related microservices, design systems, or shared tooling.
What is a polyrepo?
A polyrepo (or "multi-repo") setup gives each project its own version-control repository. This is the default model on GitHub and the historical norm — each service, library, or product lives in a separate repo with its own issues, releases, and access control.
Are monorepos faster to work in?
For atomic cross-project changes, yes — you can update an API and all its consumers in one PR. For everyday work in a single project, polyrepos are usually faster (smaller repo to clone, faster CI, less context to learn).
Do I need special tooling for a monorepo?
For more than a handful of projects, yes. Build systems like Nx, Turborepo, Bazel, or Pants compute affected projects per PR so CI does not rebuild the world on every commit. Without that tooling, monorepo CI becomes extremely slow.
What is a "trunk-based development" monorepo?
A monorepo where everyone commits to a single main branch (no long-lived feature branches), backed by feature flags and CI gates. This is the dominant pattern at Google and similar large companies — it scales well when paired with strong test culture and code review.
Can I have a hybrid?
Yes — many teams keep tightly-coupled code (e.g. frontend monorepo with web, mobile-web, and design system) in one repo, while keeping unrelated backend services in their own repos. The grouping should follow team boundaries and change-frequency, not arbitrary technical lines.
What is the most underrated cost of polyrepos?
Dependency drift. The shared library is v1.4 in repo A, v1.7 in repo B, v0.9 in repo C — and a security patch must be released to all of them. Monorepos make "one version of everything" trivial; polyrepos make it a perpetual project.