Monorepo Strategies and Management

Advanced Topics » Monorepo Strategies and Management

Advanced engineering deep-dive. This page is the conceptual hub for monorepos: what they are, how they differ from polyrepos, the core properties that make them worthwhile, and the decision of whether to adopt one. The two companion pages go deeper — Tooling & Build Systems surveys Bazel, Nx, Turborepo, Rush, Lerna, and Pants; Scaling & Engineering covers build graphs, remote execution, ownership, and VCS scaling. Helpful background: build systems and dependency graphs, CI/CD pipelines, and Git internals. For day-to-day Git workflows see the Git Reference; for pipeline fundamentals see CI/CD Pipelines.

Introduction

A monorepo (monolithic repository) is a software development strategy where code for multiple projects is stored in a single repository. This approach has gained significant traction among large tech companies and is increasingly adopted by teams of all sizes.

  • One repo, many projects. A monorepo is not a monolith. Many independently-deployable projects share one version-controlled tree, enabling atomic cross-project changes.
  • The build graph is everything. Modern monorepo tools model projects as a dependency DAG, so they can build, test, and deploy only what a change actually affects.
  • Caching makes it scale. Content-addressed local and remote caches mean a given input is built once, ever — across the whole team and CI fleet.
  • It’s a trade-off. You trade per-team autonomy and small clones for shared tooling and frictionless code reuse. Worth it when projects are coupled; costly when they aren’t.

What is a Monorepo?

A monorepo contains multiple distinct projects with well-defined relationships and dependencies, all within a single repository. Unlike a monolithic application, projects in a monorepo can be deployed independently.

The mental model that unlocks every monorepo tool is the project dependency graph. Tools like Nx, Turborepo, and Bazel parse this graph, then use it to answer the only question that matters at scale: given this change, what is the minimal set of projects I must rebuild, retest, and redeploy?

flowchart TD
    subgraph Packages
      utils["@org/utils"]
      ui["@org/ui-components"]
      api["@org/api-client"]
    end
    subgraph Apps
      web["apps/web"]
      admin["apps/admin"]
    end
    utils --> ui
    utils --> api
    ui --> web
    api --> web
    ui --> admin
    api --> admin
    web -. "change here" .-> affected1["rebuild: web only"]
    utils -. "change here" .-> affected2["rebuild: utils, ui, api, web, admin"]

A change to a leaf app rebuilds just that app; a change to a foundational package like utils cascades to everything that transitively depends on it. “Affected” commands (nx affected, turbo run --filter) compute exactly this set, which is why monorepos can keep CI fast even with thousands of projects. The mechanics of computing and exploiting that affected set are the subject of the Scaling & Engineering page.

Monorepo, Monolith, and Polyrepo

These three terms are routinely confused; pinning them down is the fastest way to understand what a monorepo actually is.

Term What it describes Deployment unit
Monolith One application built and deployed as a single unit One artifact
Monorepo One repository holding many projects Many independent artifacts
Polyrepo Many repositories, typically one project each Many independent artifacts

A monorepo is an axis of source organization, not of runtime architecture. You can ship a single monolith out of a polyrepo, or hundreds of independently-deployed microservices out of a monorepo. The defining property of a monorepo is simply that conceptually distinct, separately-deployable projects share one version-controlled tree — and therefore one commit history, one set of tooling, and one place where a single change can atomically touch many projects at once.

Core Concepts

Three properties define what a monorepo is and explain why teams reach for one. Everything else — the tooling, the caching, the CI strategy — exists to deliver or protect these three.

Single Source of Truth

In a monorepo, there is exactly one version of every internal package at any given commit. There is no “which version of @org/utils is apps/web actually running?” — the answer is whatever is on this commit, because both live in the same tree. This eliminates an entire class of problems that polyrepos spend significant effort managing:

  • No internal version skew. A consumer always builds against the current source of its dependencies, not a published snapshot that may be weeks stale.
  • No diamond dependency conflicts between internal packages. Two libraries cannot transitively demand incompatible versions of a third internal package, because there is only one version of it.
  • One canonical place to look. Cross-cutting concerns — a shared lint config, a security patch, an API contract — have a single authoritative location rather than being copied across repos.
// In a monorepo, an internal dependency is a workspace reference,
// not a pinned published version:
{
  "dependencies": {
    "@org/utils": "workspace:*"   // always the source in this tree
  }
}

The cost is that “the truth” is now large: every developer’s checkout conceptually contains the whole organization’s code. Keeping that workable is exactly what the VCS-scaling techniques on the Scaling & Engineering page address.

Atomic Cross-Project Changes

Because every project shares one commit history, a single commit (and a single pull request) can change a shared library and every consumer of it together. The change either lands as a coherent whole or not at all.

This is the property that most cleanly distinguishes a monorepo from a polyrepo. In a polyrepo, renaming a function in a shared library is a multi-step dance: change and publish the library, then open a follow-up PR in each consumer to bump the dependency and adapt to the new API, coordinating the merges so nothing breaks in between. In a monorepo it is one PR:

# One commit renames the API and updates every caller at once.
# CI builds the affected set; the change is atomic and bisectable.
git commit -am "rename formatDate -> formatTimestamp across all consumers"

Atomicity has two compounding benefits:

  • Refactoring is cheap. IDEs and codemods can find and update every usage in the tree, so large cross-cutting refactors become routine rather than dreaded.
  • History stays coherent. Every commit on main represents a buildable, internally-consistent state of the whole organization, which makes git bisect and rollbacks meaningful across project boundaries.

Unified Versioning and Consistent Tooling

A monorepo centralizes the decisions a polyrepo distributes. There is one root toolchain configuration — one TypeScript version, one linter config, one formatter, one set of CI conventions — inherited by every project unless it deliberately overrides it. Upgrading the whole organization to a new compiler is a single PR rather than a campaign across dozens of repos.

Versioning of published artifacts can still follow different policies, and the choice is a deliberate one:

// Version strategies for what a monorepo publishes externally
enum VersionStrategy {
  FIXED = "fixed",             // all packages share one version, bumped together
  INDEPENDENT = "independent", // each package carries its own semver
  GROUPED = "grouped"          // groups of related packages version together
}
  • Fixed (lockstep) versioning trades semantic precision for simplicity: every release bumps everything, so the version number stops meaning “what changed in this package.” Good for tightly-coupled suites.
  • Independent versioning preserves per-package semver, at the cost of release tooling that can compute exactly which packages changed and bump only those.

The key point is that internal dependencies need no versioning at all — they are workspace references resolved from source — so versioning becomes purely an external publishing concern rather than an everyday-development one.

Monorepo vs Polyrepo Comparison

Choosing a monorepo is choosing a set of trade-offs, not an unambiguous upgrade. The right answer depends on how coupled your projects are and how much your teams need isolation from one another.

Monorepo Advantages

  • Atomic Changes: Refactor across multiple projects in one commit
  • Shared Code: Easy code reuse without package publishing
  • Consistent Tooling: Single set of build tools and configurations
  • Simplified Dependencies: No version conflicts between internal packages
  • Better Refactoring: IDEs can find and update all usages

Polyrepo Advantages

  • Independent Versioning: Each project has its own release cycle
  • Smaller Repositories: Faster cloning and operations
  • Clear Boundaries: Enforced separation between projects
  • Flexible Tech Stacks: Each repo can use different tools
  • Granular Access Control: Per-repository permissions

Comparison Table

Aspect Monorepo Polyrepo
Code Sharing Direct imports Published packages
Atomic Changes Native Requires coordination
Build Complexity Higher Lower per repo
Repository Size Large Small
Team Autonomy Lower Higher
Tooling Investment High upfront Lower initial

A useful way to read this table: a monorepo moves complexity from coordination into tooling. Polyrepos keep each repository simple but push complexity into the spaces between repos — publishing, version negotiation, and multi-repo change coordination. Monorepos collapse that inter-repo complexity but demand graph-aware build tools and VCS scaling techniques to stay fast. You are choosing where the hard problems live, not whether you have any.

When to Adopt a Monorepo

The deciding question is coupling: how often do changes need to cross project boundaries, and how much code do projects genuinely share? The more your projects move together, the more a monorepo’s atomicity and single-source-of-truth pay for their tooling cost.

Consider a monorepo when:

  • You have multiple projects that share code
  • Teams frequently collaborate across projects
  • You need atomic commits across multiple projects
  • Consistent tooling and standards are important
  • You want simplified dependency management

Avoid a monorepo when:

  • Projects have completely different tech stacks
  • Teams require strict access control separation
  • Projects have vastly different release cycles
  • Your VCS struggles with large repositories
flowchart TD
    start{Do projects<br/>share code or<br/>change together?}
    start -- "Rarely / never" --> poly[Polyrepo<br/>keep teams autonomous]
    start -- "Frequently" --> stack{Same-ish tech stack<br/>and tooling?}
    stack -- "No, wildly different" --> poly
    stack -- "Yes" --> access{Need hard per-team<br/>access isolation?}
    access -- "Yes" --> poly
    access -- "No" --> mono[Monorepo<br/>invest in graph tooling]

Adopt gradually, not all at once

A monorepo is rarely worth a big-bang migration. The lower-risk path is to start a monorepo with the few projects that are most tightly coupled, prove out the tooling and CI on them, and migrate additional projects only as the coupling (and the pain of keeping them separate) justifies it. The mechanics of polyrepo-to-monorepo migration — preserving history with git subtree/git filter-repo, rewriting import paths, and wiring up the workspace — live on the Scaling & Engineering page.

Going Deeper

This hub covers the what and whether of monorepos. The two companion pages cover the how: the tools that make a monorepo more than a large folder, and the engineering that keeps it fast at scale.

Tooling & Build Systems

Pick and configure the build tool

  • The three tiers: affected-graph runners, package managers, hermetic build systems
  • Nx, Turborepo, Lerna, Rush, Bazel, Buck2, Pants compared
  • Workspace configuration and internal dependency wiring
  • Computation caching and remote-cache internals
  • A decision guide keyed to language mix and scale

Read this when: choosing or configuring a monorepo tool

Scaling & Engineering

Keep it fast as it grows

  • Build graphs and affected-target analysis
  • Distributed remote execution and parallelism
  • Dependency-visibility rules and enforced boundaries
  • Code ownership (CODEOWNERS) and review policy
  • CI strategy and VCS scaling (sparse checkout, partial clone, VFS)

Read this when: operating a monorepo at scale

Real-World Case Studies

The largest monorepos in industry validate the core concepts above — and show that delivering them at scale requires substantial custom tooling.

Google

  • Size: 2+ billion lines of code
  • Tool: Bazel (originally Blaze)
  • Strategy: Single massive repository
  • Benefits: Unified tooling, atomic changes
  • Challenges: Custom VCS (Piper), specialized tools

Facebook (Meta)

  • Size: Hundreds of millions of lines
  • Tool: Buck (now open source)
  • Strategy: Mercurial-based monorepo
  • Benefits: Rapid iteration, code sharing
  • Challenges: Performance at scale

Microsoft

  • Project: Windows codebase
  • Tool: Git with VFS (Virtual File System)
  • Strategy: Git-based monorepo
  • Benefits: Unified Windows development
  • Challenges: 300GB+ repo size

Uber

  • Migration: Polyrepo to monorepo (2018)
  • Tool: Bazel
  • Languages: Go, Java, JavaScript
  • Benefits: 50% reduction in build times
  • Results: Improved developer productivity

Key Takeaways

  • A monorepo organizes source, not runtime. It is about one repository of many independently-deployable projects — orthogonal to whether you ship a monolith or microservices.
  • Single source of truth removes version skew. One version of every internal package per commit eliminates internal diamond conflicts and “which version is running?” entirely.
  • Atomic changes are the killer feature. One commit can change a library and every consumer together, making cross-cutting refactors routine and history coherent.
  • It moves complexity, it doesn’t remove it. Inter-repo coordination cost is traded for graph-aware tooling and VCS-scaling cost. You choose where the hard problems live.
  • Coupling is the deciding question. Adopt when projects share code and change together; keep polyrepos when teams need autonomy, isolation, or divergent stacks.
  • Adopt gradually. Start with the most coupled projects, prove the tooling, and migrate the rest as the trade-off justifies it — not in a big bang.

See Also

See Also

Monorepo Companion Pages

Related Advanced Topics

Applied Technology

External Documentation