Monorepo vs Polyrepo
Atomic changes, tooling, ownership, build scale, access control and release independence — six axes that pull in different directions. There is no universal answer, and anyone who gives you one is describing their last job.
The requirement, the obvious build, and why it breaks
Every lesson starts where the work starts: someone asked for something, and the first implementation that comes to mind survives until the requirement changes.
One repository or many, and which of the arguments people make about this actually apply to us?
Six teams, eleven deployable services, and a shared domain library. Cross-cutting changes currently take three weeks of coordinated version bumps. Someone has proposed a monorepo.
Big companies use monorepos and they are clearly successful, so a monorepo is the mature choice. One repository, one version of everything, atomic changes across the whole system.
The atomic change is real and it is bought with tooling. A monorepo without affected-target build selection, remote caching and a code-ownership mechanism is slower to work in than the polyrepo it replaced, and every one of those is a system somebody has to run — the build-system side of this belongs to DevOps and is linked below.
- The atomic change is real and it is bought with tooling. A monorepo without affected-target build selection, remote caching and a code-ownership mechanism is slower to work in than the polyrepo it replaced, and every one of those is a system somebody has to run — the build-system side of this belongs to DevOps and is linked below.
- The examples cited have build infrastructure teams of dozens of people. Adopting the layout without the investment gets you the costs and none of the benefits, and this is the single most common way this decision goes wrong.
- "One version of everything" is a genuine benefit and also an obligation: everyone upgrades when you upgrade, so a library change is now a synchronised event across six teams rather than six independent ones (API Stability).
- The mirror-image error is just as common: teams split into eleven repositories to get independence, and discover that the shared library now has eleven live versions and every cross-cutting fix is a three-week bump chain.
- Both narratives skip the axis that decides most real cases, which is not technical: which shape matches how the teams actually coordinate.
What limits the solution, and what must never stop being true
This domain leads with these two. A design that ignores its constraints is not a design, and an invariant nobody named is one nothing is protecting.
- The current repositories have years of history and independent CI; whichever way this goes, the migration is real work.
- Two of the eleven services have compliance requirements that restrict who may read their source (Trust Boundaries).
- The build system available to this team is what it is; monorepo advice assumes tooling that has to be paid for in engineering time if it does not exist.
- A change that must be applied consistently across services must have some mechanism that makes inconsistency detectable. Whether that is a single build or a contract test is the decision, but it cannot be nothing (API Stability).
- Every service remains independently deployable regardless of repository layout, or you have coupled release to source control by accident.
Who owns what, and where the seams fall
Responsibilities decide boundaries; boundaries decide what an interface has to say.
- The repository layout owns making a certain class of change cheap, and it always makes the complementary class expensive. Deciding which class you have more of is the entire decision.
- Whoever chooses a monorepo owns the tooling bill: build selection, caching, ownership routing, and a CI that does not run everything on every commit. It is felt daily, by everyone, in wall-clock time.
- Whoever chooses a polyrepo owns the coordination bill: version discipline, contract tests, and a mechanism for propagating a cross-cutting change (Semantic Versioning).
- Nobody owns the hybrid by default, which is why hybrids drift — a per-team monorepo with three stragglers is a common and unstable end state.
- A repository boundary is a *transaction* boundary for change: what can be modified in one commit, reviewed as one unit and reverted as one unit.
- It is not a module boundary, and conflating them is the deepest confusion here. A monorepo can hold beautifully isolated modules and a polyrepo can hold a distributed ball of mud (The Modular Monolith).
- It is also not a deployment boundary. Independent deployability is a property of the artefacts and the pipeline, not of where the source is stored — release and deployment are separable, and DevOps owns that distinction.
- It is genuinely an access-control boundary, and that is the one axis where a polyrepo has a capability a monorepo has to simulate with tooling.
Six axes, and where each one flips
The argument is usually conducted on one axis — whichever one the loudest person last got burned by. Laying out all six makes visible that neither option wins, and that the weighting is where the real disagreement lives.
Read the last column first. It is the only part of this table that is about *your* organisation rather than about repositories in general, and it is what turns a preference into a decision.
- The first two rows are in direct tension: a monorepo buys atomicity with tooling, and if the tooling is missing you have bought nothing at a very high price.
- The third row is the one people misstate. A monorepo gives visibility, not ownership; a polyrepo gives enforcement, not clarity.
- The fifth row is the only axis with a hard answer, and it is the one most often left out of the discussion entirely.
| Axis | Monorepo | Polyrepo | What decides it for you |
|---|---|---|---|
| Atomic cross-cutting change | One commit, one review, one revert | N pull requests over weeks, multiple versions live | How often do changes actually cross service boundaries? Count last quarter's. |
| Tooling required | Affected-target selection, remote cache, ownership routing — all must be built or bought | Whatever your language's default does, out of the box | Can you staff a build-infrastructure capability at all? If not, this axis decides everything. |
| Ownership clarity | Needs an explicit mechanism; everything is visible to everyone | Implicit and enforced — you cannot edit what you cannot clone | Do you want visibility or enforcement? They are opposites here. |
| Build and CI scale | Grows with the whole repository unless selection is solved | Naturally bounded per repository | What is CI wall-clock for a one-line change? Measure it, do not estimate it. |
| Access control | Simulated with tooling, usually imperfectly | Native and absolute | Do you have source that some engineers must not read? If yes, this is close to decisive (Least Privilege as a Design Decision). |
| Release independence | Available, but requires deliberate pipeline design | Free by default | Not really a repository question — but it becomes one when the pipeline is naive. |
Scored, for one specific organisation
Scoring is worth doing once, out loud, for the situation actually in front of you — six teams, eleven services, one shared library, and no build-infrastructure capability. The numbers below are for that case and are not transferable, which is itself the lesson.
Notice that the option most people would call the compromise does not win, and the option that wins here would lose badly at ten times the size.
| Option | Simplicity | Flexibility | Testability | Operational | Migration cost | Note |
|---|---|---|---|---|---|---|
| Stay polyrepo, invest in contract tests and a propagation bot | Cheapest by a wide margin because it changes nothing structural. Attacks the actual pain — three-week bump chains — with two weeks of work rather than two quarters. Does not deliver atomicity, ever. | |||||
| Full monorepo with bought build tooling | Delivers the atomicity and one version of everything. Costs a migration, a permanent tooling dependency, and an access-control problem for the two compliance-restricted services. Right at three times this size; premature here. | |||||
| Full monorepo, no special tooling | The failure everyone warns about: forty-minute CI on every commit, engineers batching changes, and the atomicity benefit consumed by daily friction. Looks like the mature choice and is the worst row here. | |||||
| Per-domain monorepos — three repositories along team boundaries | Gets atomicity where changes actually cross (inside a domain) and keeps each repository within default tooling. Costs a boundary that must be defended, and it is wrong the moment the org chart changes. |
caveat These numbers are for one organisation at one moment and every row moves with two things the axes cannot hold. First, whether a build-infrastructure capability exists: give this organisation two platform engineers and rows two and three swap places entirely. Second, the frequency of cross-cutting change, which is the only real argument for atomicity and which nobody in the room usually knows — measure it before scoring. There is no measurement in these scores; they exist so the axes can be argued about in the same units, and a lesson that presented them as a result would be selling something.
The question underneath the question
Most repository arguments are proxies for something else: a team that cannot get another team to upgrade, a build that is too slow, an ownership boundary that nobody wrote down. Naming the real complaint usually reveals a cheaper fix than moving every repository.
That is not an argument against ever changing the layout. It is an argument for making the change against a named problem, so that afterwards you can tell whether it worked.
What specific pain is this meant to fix, and is the layout the cheapest thing that fixes it?
when A shared library change has to reach six teams, and it does so by six independent version bumps.
cost The monorepo does fix this. So does a propagation bot plus contract tests, for two weeks instead of two quarters — try that first, and if it fails, you now have evidence rather than a preference (Deprecation).
when Reviews go to the wrong people and nothing has a maintainer.
cost Layout will not fix it. This is an ownership mechanism problem and it is unsolved in both layouts (Code Ownership).
when Engineers wait, or batch changes to avoid waiting.
cost A monorepo makes this strictly worse without affected-target selection. Fix the CI, then revisit — and note that if you can fix CI selection, you have just built the main prerequisite for a monorepo.
when Coupling that no interface sanctions, discovered during incidents.
cost A repository split does enforce this, absolutely and bluntly. It also converts every legitimate cross-team change into a release negotiation, so it is a real fix with a real ongoing bill (Internal Module Contracts).
when Compliance or contractual restriction on specific services.
cost This is close to decisive for keeping those services separate, whatever you do with the rest. Simulating it inside a monorepo is possible and is a permanent tooling obligation (Trust Boundaries).
when The proposal is that the current layout is not what mature companies do.
cost Do nothing. A migration with no named problem cannot succeed, because there is no outcome that would count as success (YAGNI, With Its Bill Attached).
How to build it
Most important first.
- Score the six axes against your own situation rather than in general: atomic cross-cutting change, tooling investment available, ownership clarity, build scale, access control, release independence.
- Weight them by frequency. If cross-cutting changes happen twice a year, the atomicity argument is worth very little; if they happen weekly, it dominates everything else.
- Count the tooling you would have to build, honestly, in engineer-weeks. This number is what separates the teams for whom a monorepo is transformative from those for whom it is a slow catastrophe.
- Consider the middle: a monorepo per team or per bounded context is a legitimate and common answer, and it gets atomicity where changes actually cross while keeping repositories small enough for default tooling (Module Granularity).
- Whichever you choose, invest in the thing it makes hard. Monorepo: ownership routing so reviews reach the right people. Polyrepo: contract tests and a propagation mechanism so a shared change does not take a quarter (Contract Tests).
- Write the decision down with the trigger that would reverse it, because this one gets relitigated every eighteen months by whoever joined most recently (Decision Records).
What the next change costs
The field this whole domain exists for. A structure is only better if it makes the change after this one cheaper — and it is worth saying which changes it does not help.
- Monorepo, cross-cutting change: rename an interface used by six services in one commit, reviewed as one unit, reverted as one unit. Hours. This is the strongest single argument and it is genuinely strong.
- Polyrepo, the same change: publish the new version, then six pull requests in six repositories, merged over weeks, with multiple versions live throughout and a compatibility window you have to design (Expand and Contract).
- Monorepo, everyday change to one service: cost depends entirely on tooling. With affected-target selection it is identical to the polyrepo. Without it, every engineer pays the whole repository's CI time on every commit, forever, and that cost is paid thousands of times a year.
- Polyrepo, everyday change to one service: cheap and independent, which is why it feels good early and why teams underestimate the cross-cutting cost they are accumulating.
- Migration either way: weeks of work plus a permanent discontinuity in tooling and history. Both directions are expensive enough that the decision deserves an hour of honest scoring rather than an afternoon of preference.
- A monorepo trades everyday build speed and access granularity for atomicity and consistency, and pays for the first two with a permanent tooling investment most teams do not staff.
- A polyrepo trades cross-cutting change speed for independence, and pays with version sprawl that is invisible until a security fix needs to reach everything.
- The hybrid trades a bit of both for a boundary you have to keep defending, and it needs an explicit rule about what gets its own repository or it drifts.
What can go wrong
- The monorepo is adopted without build selection, CI takes forty minutes on every commit, and engineers start batching changes — which makes every failure harder to attribute, because a batched commit has more than one candidate cause.
- The polyrepo produces a shared library with eleven live versions, and a security fix has to be released eleven times by eleven teams over a quarter.
- A migration is done half-way and abandoned, leaving a large repository plus five stragglers and two competing sets of tooling — reliably the worst of the available outcomes.
- Access control in the monorepo is solved by "we trust everyone", which is fine until the first compliance audit asks who can read the payment service's source.
- The team that migrated attributes every subsequent improvement to the repository change, and every subsequent problem to something else, so nobody ever learns whether it worked.
- A monorepo makes your daily experience depend on the build system's ability to test only what changed. Without that, CI time grows with the repository and every engineer pays.
- A polyrepo makes cross-cutting change depend on version propagation, which depends on every consuming team's willingness to upgrade (Deprecation).
- Both make ownership depend on an explicit mechanism, because in neither layout does the directory structure tell you who to ask (Code Ownership).
- "Google and Meta use monorepos, so monorepos scale." They scale *with* thousands of engineer-years of build infrastructure. The layout and the investment are the same decision and citing one without the other is the core error here (When Design Does Not Pay).
- "Microservices need separate repositories." Independent deployability comes from the artefacts and the pipeline. Plenty of organisations run many services from one repository, and the coupling that hurts is in the interfaces, not the source layout (The Modular Monolith).
- "A monorepo will fix our coupling." It will make the coupling visible and easier to change, which is genuinely useful and is not the same as removing it. A tangled system in one repository is a tangled system you can now refactor atomically (Kinds of Coupling).
- "We can decide this later." You can change it later, at the cost of weeks and a tooling discontinuity. It is a reversible decision with a real price, which is a different thing from a cheap one (Reversible and Irreversible Decisions).
Testing it, and how it ages
- In a monorepo, the test that matters is CI wall-clock time for a one-line change to one service. If it grows with the repository, the tooling is not there yet and no amount of layout will fix it.
- In a polyrepo, the test that matters is contract tests between services, because nothing else catches an interface drift until deployment (Contract Tests).
- In both, a periodic check that no shared library has more than N live versions in production tells you whether the coordination mechanism is working (Dependency Management).
- Most organisations oscillate. A monorepo that outgrows its tooling splits; a polyrepo that outgrows its coordination consolidates; and each swing is usually attributed to the layout rather than to the missing investment.
- The stable end states are small-and-many with strong contracts, or large-and-one with strong tooling. The unstable middle is a handful of medium repositories with neither, which is where most organisations actually are.
- What forces a change is usually not engineering but the org chart: a reorganisation that changes who owns what makes the old boundaries wrong overnight (Code Ownership).
Where this applies
This domain's advice is contested more than most. These labels say what each claim is specific to — and where CONTESTED appears, the note gives the strongest form of the opposing view rather than a caricature.
- SCALE-SPECIFICThis is the module's clearest case. At eight engineers and three services, a single repository with the default tooling is obviously right and the entire monorepo literature is irrelevant — you have atomicity for free and no build problem to solve. Between roughly thirty and three hundred engineers the decision is genuinely hard and turns on how much build tooling you can staff. Above that, both extremes work and the failure mode is the unstable middle. Monorepo advice from a ten-thousand-engineer company is not merely unhelpful at eight engineers, it is actively harmful, because it recommends infrastructure that will consume the whole team.
- CONTESTEDThe strongest polyrepo case, stated properly: repository boundaries are the only boundary that is actually enforced by default, so a polyrepo gets you real modularity for free, while a monorepo gets you modularity only as long as somebody maintains the lint rules that stop teams reaching into each other's internals — and those rules are always weaker than a missing checkout. Its advocates point out that most monorepo horror stories are about coupling that a repository boundary would have prevented outright. The counter is that this "free" boundary is also what makes a cross-cutting fix take a quarter, and that the coupling reappears as version skew.
- SIMPLIFIEDTreated here as a binary for teaching. Real organisations run gradients: a monorepo with vendored external code, a polyrepo with a shared meta-repository, per-domain monorepos with a published core. Those middles are frequently the right answer and are covered here only as "the hybrid", which understates how much design each one needs.
Where the depth lives
This domain teaches the codebase-level structure and hands the rest off.
- — System Design — the arguments here rhyme with service decomposition but are not the same decision: repository topology governs how a change is made, service topology governs how the system runs, and conflating them is how organisations end up with eleven repositories and one deployment.