Team Workflows
Topic 45

Monorepos vs Polyrepos

Workflow

A monorepo holds many projects in one repository; a polyrepo gives each project its own. The decision trades atomic cross-project changes and unified tooling against build and CI complexity and a larger blast radius when something breaks.

Neither shape is correct in the abstract. The deciding question is how tightly your projects are coupled: things that change together want to live together, and things that deploy independently want their own boundaries.

The Two Shapes

A monorepo is one repository with many packages or services inside it; a polyrepo is many repositories, one project each. What "one repo" actually changes is the unit of atomicity — in a monorepo a single commit can touch five projects at once, which is impossible across repository boundaries.

The Monorepo Case

The monorepo's strongest argument is the atomic cross-package commit: a change spanning five services becomes one PR that lands all-or-nothing. It also gives one source of truth for dependency versions and lets every project share the same CI and tooling.

This is why refactors that ripple across projects are far easier in a monorepo — you change the API and all its callers in the same diff, so the tree is never in a half-migrated state.

The Monorepo Cost

The cost is that CI must scale to the whole tree. Without affected-only builds, every commit rebuilds everything, so a one-line README change can trigger the full suite. Clones grow, permissions get coarser, and a build tool like Bazel, Nx, or Turborepo stops being optional.

Access control is the subtler tax: a monorepo grants visibility at the repo level, so everyone with access sees every directory. You cannot hide one team's code the way separate repos do.

The Polyrepo Case

A polyrepo gives each project independent versioning, clean per-repo permissions, and CI scoped to just that project. The cost is the mirror image of the monorepo's strength: a cross-cutting change becomes coordinated PRs across several repos, with no atomicity and a real chance they merge out of order into a broken state.

GitHub-Specific Constraints

On GitHub the shapes have concrete consequences. A monorepo leans on per-path CODEOWNERS to recover ownership boundaries, paths filters in Actions to avoid rebuilding everything, and sparse checkout to keep clones manageable. Org-wide search also favors monorepos, since everything is in one place to grep.

Monorepo vs Polyrepo

Monorepo — makes a change spanning five services one atomic PR, unifies dependency versions and tooling, and simplifies refactors; but CI must build only what changed (Bazel, Nx, Turborepo), clones grow, and access control is path-based rather than repo-based.

Polyrepo — gives each project independent versioning, clean per-repo permissions, and fast scoped CI; but a cross-cutting change becomes coordinated PRs across repos with no atomicity. Choose monorepo for tightly-coupled projects that change together, polyrepo for independently-deployed services with separate owners.

Common Mistakes
  • Running full CI on every commit in a monorepo, so without paths filters or affected-only builds a one-line README change rebuilds the entire tree.
  • Splitting tightly-coupled services into polyrepos, so a single feature now needs three coordinated PRs that can merge out of order and break.
  • Putting everything in a monorepo with no build tool, so clone times and CI duration grow until the repo is hostile to work in.
  • Assuming per-repo permissions in a monorepo, when everyone with access sees everything and you cannot hide one team's directory.
  • Versioning monorepo packages independently by hand, so without Changesets or Nx the version graph drifts out of sync.
Best Practices
  • In a monorepo, gate Actions with paths filters and use affected-only builds (Nx, Turborepo, Bazel) so CI scales.
  • Use per-directory CODEOWNERS to recover the ownership boundaries a monorepo flattens.
  • Choose a monorepo for projects that change together and polyrepos for independently-deployed, separately-owned services.
  • Manage monorepo package versions with a tool like Changesets rather than by hand.
  • In a polyrepo, document the cross-repo change order so coordinated PRs do not merge into a broken state.
Comparable toolsNx / Turborepo / Bazel affected-only monorepo buildsLerna / Changesets monorepo package versioningGitLab hosts either shapeBitbucket / Azure DevOps repo-shape agnostic

Knowledge Check

What capability does a monorepo give that a polyrepo cannot?

  • An atomic commit spanning many projects, landing a cross-cutting change all-or-nothing in one PR
  • Independent per-project version numbers that advance on their own with no cross-project coordination needed
  • Per-project access control where each directory is walled off and has no shared visibility across teams
  • CI that automatically scopes itself to one project with no configuration

Why do monorepos require affected-only CI?

  • Otherwise every commit rebuilds the whole tree, so even a one-line README change runs the full suite
  • Because Git cannot diff a monorepo without a build tool like Nx or Bazel computing the change set first
  • Because CI is billed per repository, and a monorepo counts as many separate repos toward that quota
  • They do not — monorepos make CI inherently faster by sharing one cache across the whole tree

What is the permission tradeoff of a monorepo?

  • Access is repo-level, so everyone with access sees every directory; you cannot hide one team's code
  • Each directory gets its own login credential, making access harder to grant as the tree grows
  • Only the repo owner can read any file, so contributors must request a copy of each directory
  • There is none — monorepos support per-directory read and write permissions natively out of the box

What does splitting tightly-coupled services into polyrepos cost you?

  • A single feature becomes coordinated PRs across repos that can merge out of order and break
  • The services can no longer be deployed independently once they each live in their own repository
  • Git refuses to clone more than one related repo at a time onto a single developer machine
  • Nothing — coupled services are always better in separate repos, since isolation outweighs atomic commits

You got correct