tech, developers, and the code underneath

issue 068· essay·

The monorepo question has a boring answer

It's not about repository count. It's about whether you can make an atomic change across your system.

The monorepo debate is one of the few technical arguments that has been running for fifteen years without either side updating. That usually means both sides are arguing about the wrong thing.

the actual question#

Not "one repository or many." The question is:

Can you make a change that spans multiple components, atomically, with a single review and a single test run?

If yes, you have the property that matters. If no, you do not, regardless of how many repositories you have.

You can have one repository where every service has its own pipeline, its own version, and its own release train — and get none of the benefit, because a cross-cutting change still requires coordination. That is a polyrepo wearing a monorepo's directory structure.

You can have separate repositories with a workspace tool that checks them all out and builds them together, and get most of the benefit.

what atomicity buys#

Refactoring at scale becomes possible. Renaming a function used by twelve services is one commit. In a polyrepo it is twelve pull requests, a deprecation period, a compatibility shim, and a cleanup task nobody does. This is the single biggest difference and it compounds over years — polyrepo codebases accumulate interface debt because changing an interface is expensive.

One version of every dependency. No "service A is on library v2, service B is on v4, and the shared types are subtly incompatible." Upgrading is a single change that either passes CI everywhere or does not.

Discoverability. Grep works. Find-references works. A new engineer can read the whole system.

what it costs#

Tooling. Above a certain size, standard tools break. git status gets slow. Your IDE indexes forever. CI running everything on every commit becomes infeasible. You need build graph analysis to test only what changed. That is real infrastructure investment and it never ends.

Coupling by default. Shared code is easy, so shared code happens, and suddenly a change to a "utility" function affects nine teams. Polyrepo makes coupling expensive, which is a crude but effective forcing function.

Access control. One repository means one permission boundary, mostly. Fine for a company, not fine for open source with contractors.

Blast radius on the build system. Everyone depends on it. When it breaks, everyone stops.

the honest heuristic#

  • Under ~50 engineers: monorepo, almost always. The tooling cost has not kicked in and the coordination benefit is immediate.
  • 50 to 500: monorepo if you are willing to staff a build/platform team. If you are not, the tooling debt will eat you.
  • Over 500: whatever you already have, improved. Migrating a large organization between these models is a multi-year project with a poor track record, and the energy is better spent on making the current arrangement work.
  • Open source with external contributors: polyrepo, usually. The access control and contribution-scope arguments dominate.

the thing both sides get wrong#

Monorepo advocates point at Google and Meta. Those companies have hundreds of engineers working full-time on build infrastructure. You do not. Their approach is not transferable without their investment, and citing them is like citing an F1 team's pit strategy for your commute.

Polyrepo advocates point at microservice independence. Then they build a shared library, and a shared types package, and a shared client SDK, and now every service is coupled through three packages with independent release cycles and version skew — which is strictly worse than the coupling they were avoiding.

what to actually do#

Whichever you have, invest in the property you are missing.

In a monorepo: enforce boundaries. Ownership files, dependency rules that fail the build, and a rule that a module's public API is explicit. Physical colocation must not mean logical coupling.

In a polyrepo: invest in cross-repo change tooling. A script that opens coordinated pull requests. A dependency dashboard. A way to run integration tests across repositories before merge. This is unglamorous and it is the difference between a polyrepo that works and one that slowly ossifies.

The repository count is a detail. The workflow is the thing.

get README in your inbox

One dispatch, no noise. Tech and developer news, plus the occasional long piece on the craft.

subscribe →