Monorepo vs Polyrepo: How to Choose the Right Setup
Summary
Monorepo vs polyrepo comes down to coupling, not preference. Monorepos win when teams share code and deploy together; polyrepos win when teams are genuinely independent. Modern tools like Nx and Turborepo have eliminated the old build-time objection. Most mature teams land on a hybrid: one repo for tightly coupled product surfaces, separate repos for truly independent services. Four questions help you decide which applies to your situation.
Monorepo vs polyrepo is a debate that resurfaces every time your engineering team grows faster than your build times. The short answer: a monorepo wins when teams share code regularly and coordinate changes across a common surface; a polyrepo wins when teams are genuinely independent and deploy without ever waiting on each other. Everything in between depends on how tightly coupled your services actually are, and how much coordination overhead you are willing to absorb. This article walks through the real trade-offs, the signals that point one way or the other, and a practical framework for making the call.
The real question is about coupling, not about repo count
Most monorepo vs polyrepo debates start with the wrong question. The number of repos is not the issue. The issue is whether your teams need to move together.
If Team A's changes routinely require Team B to update something on the same release cycle, you have tightly coupled work. One repo for that is almost always the cleaner answer. If Team A and Team B ship on their own schedules, touch different parts of the system, and never wait on each other, separate repos make genuine sense.
The worst possible outcome: two separate repos for code that still needs to deploy together. You get all the coordination cost of a monorepo with none of the benefits. Your teams end up opening PRs in repo A and repo B simultaneously, hoping both pass CI at the same time. This pattern is surprisingly common in organizations that chose polyrepo early and then let their services grow more interdependent than originally planned.
The first step before choosing anything is to draw a simple dependency map. List every service or package you own and draw arrows where one depends on another. If the arrows form a dense cluster, that cluster belongs in one repo.

Why Google, Meta, and Microsoft all landed on monorepos at scale
Google keeps most of its code in a single repository estimated at over 80 TB of data and 2 billion lines of code. Meta and Microsoft do the same for their core product surfaces. This is not because big companies love complexity - it is because at scale, keeping dependencies in sync across hundreds of repos becomes a full-time engineering problem.
A single API change in a shared library might require coordinated updates across 40 different repos. In a monorepo, that is one pull request. One review. One merge. One rollback if something goes wrong.
According to research published by Sourcegraph, 63% of companies with 50 or more developers now use a monorepo for at least part of their codebase, up sharply from figures three years earlier.
The concrete benefit that matters most: atomic commits. When a breaking change in your API also requires frontend and backend updates, a monorepo lets you land all three changes in one PR, test them together, and roll back all three as a unit if something goes wrong. With polyrepo, you open three separate PRs, wait for three separate review cycles, and manage a window where your services are running mismatched versions.
One engineering team reported moving from occasional deployments to more than 40 app releases per week after migrating their frontend packages into an Nx monorepo. Angular upgrades that previously took months became routine tasks that teams handled without stopping other work.
Polyrepo's hidden coordination tax
Polyrepo has real advantages worth taking seriously. Each team controls its own CI/CD pipeline, its own release cadence, its own access controls. Security and compliance teams often prefer it: access to code can be restricted to named personnel on a per-repo basis, which matters in regulated industries. Open-source components live more cleanly in their own public repos without exposing internal code.
For teams that are genuinely independent, these advantages are real. A small services team that owns a data pipeline running on its own schedule and touching no shared code has little to gain from sitting inside a larger monorepo.
But polyrepo organizations quietly accumulate costs that did not show up in the original decision. Dependency versioning across repos becomes its own discipline. A shared utility library goes stale in some repos while staying current in others. Someone has to maintain a compatibility matrix just to know which version of which library works with which version of which service.
One metric worth knowing: median PR cycle time in monorepos runs around 19 hours, versus about 2 hours in polyrepos. PRs in monorepos tend to be larger because they touch more surfaces, which slows review. But when a polyrepo change requires three coordinated PRs across three repos to land a single feature, the aggregate cycle time often exceeds the monorepo figure anyway - with the added risk of partial merges leaving services in inconsistent states.
The pattern that catches teams off guard: "we started with separate repos for independence, but now every release requires opening PRs in four places." At that point you have the worst of both worlds.
Build times stopped being the deciding factor in 2024
The classic objection to monorepos was build time. If your repo has 200 packages and you change one file, do you really want to rebuild all 200?
This argument expired somewhere around 2022 to 2024, depending on your stack.
Modern build tools use content-aware caching. They only rebuild what changed, and skip anything with identical inputs. A change to the payments package does not trigger a rebuild of design-system if nothing in design-system changed. GitHub Actions provides 10 GB of free cache storage before you need a paid remote cache solution.
Turborepo handles most JavaScript and TypeScript teams well up to around 20 packages. The configuration is a single turbo.json, remote caching works on Vercel for free or self-hosted, and the learning curve is low enough that a team can be productive within a day.
Past 20 packages, or when the build spans multiple languages, Nx's more structured approach tends to justify the steeper learning curve. Nx adds code generation, automatic CI distribution, and fine-grained dependency tracking that becomes increasingly valuable as the repo grows.
For very large organizations running thousands of packages across multiple languages, Bazel (Google's open-source build system) is the only tool designed for that scale. The setup cost is significant and almost always requires a dedicated platform engineering team.

Four questions that settle the debate for your team
There is no universal right answer, but there is a reliable framework.
1. Do your teams share code that changes frequently? If yes - a shared design system, a shared API client library, shared authentication logic - a monorepo is almost always the better choice. Keeping a shared library synchronized across multiple repos requires constant discipline and almost always drifts.
2. Do your teams deploy independently? If Team A's service ships on Tuesday and Team B's ships on Thursday without any coordination needed, polyrepo earns its place. If releasing one service requires a simultaneous release of another, that coupling cost belongs in the equation.
3. How many engineers are touching this codebase? Below 20 engineers, repo structure matters less than you think. Above 50, the coordination costs of polyrepo start accumulating visibly. Above 200, the case for a monorepo becomes very strong unless teams are genuinely siloed by product and technology.
4. Do you have security or compliance requirements that isolate code per team? In regulated industries - finance, healthcare, some government contexts - access to code may need to be restricted to named personnel. That constraint can override the other answers. If hard code isolation is a compliance requirement, polyrepo is not a choice, it is a constraint.
If you answered "yes" to question 1 and "no" to question 4, a monorepo is almost certainly the right call. If you answered "no" to question 1 and "yes" to question 2, polyrepo is a defensible choice. Everything else is a judgment call.
What most growing teams actually land on
Very few mature engineering organizations operate at either extreme. The pattern that appears most often: a monorepo for the core product (frontend, shared libraries, backend services that depend on each other), and separate repos for truly independent components.
A data pipeline that runs on its own schedule and shares no code with the main product belongs outside the monorepo. An internal tool maintained by a single-person team belongs outside. An open-source library that needs public visibility belongs outside.
This is not a compromise. It is the correct answer to a question that rarely has a clean binary answer. Keep things together that need to move together. Separate what is genuinely independent.
The important thing is to make the decision deliberately, not by default. Most teams that end up with polyrepo sprawl did not choose it: they started one service, then another, then another, and never stopped to ask whether those services needed to live together.
The tooling that makes either approach actually work
For monorepos:
Nx: strongest feature set, good for polyglot builds and teams past 20 packages. Higher learning curve but scales further.
Turborepo: simpler to start, excellent for JS/TS teams under 20 packages. Native Vercel caching or self-hosted options.
Bazel: for very large multi-language repos at enterprise scale. Significant setup investment required.
For polyrepos:
A versioning discipline for shared libraries is not optional. Without semantic versioning and a consistent changelog process, dependency drift becomes the norm within six months.
CI/CD tooling that triggers cross-repo pipelines when a shared dependency changes. GitHub Actions supports this via
workflow_dispatchandrepository_dispatchevents between repos.A dependency registry (npm private registry, GitHub Packages, Artifactory) to manage shared package distribution.
Biscuit can fetch the config file. The call on which one to use still comes down to those four questions.
