Git, CI/CD & GitOpsXXVIII · Monorepo vs Multi-RepoArchitecture
Monorepo architecture — one repository, many projects
What you'll learn
- Describe the physical shape of a monorepo and what it does to the working tree
- Identify the build-system and ownership patterns that keep a monorepo tractable
- Recognise the operational costs and the sparse-checkout patterns that mitigate them
- Recall the published examples at Google, Meta, and Microsoft and the scale each operates at
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
A monorepo is a single repository that physically contains many
projects. The trunk is one, the history is one, the branch
protection is one - but the units of work are individual
directories, individual ownership rows, and individual CI
matrix entries. The whole model rests on the assumption that
the team can solve the discoverability problem: given an
engineer who works on directory services/payments/, how does
the repository give them a fast working tree, a fast build,
and a fast review, without paying the cost of cloning the
entire history?
The physical shape
A monorepo is one .git directory, one HEAD, one set of
branches, and one working tree at any moment. Under the root
sits every directory the team owns: Terraform modules,
Ansible roles, Kubernetes manifests, application code,
generated clients, documentation. The unit of merge is the
whole trunk; the unit of build is the directory a CI matrix
entry declared; the unit of ownership is a row in
CODEOWNERS that matches a path.
flowchart LR
subgraph MR["Monorepo root"]
T["terraform/"]
A["ansible/"]
K["kubernetes/"]
S["services/"]
D["docs/"]
end
MR --> CI[CI matrix per directory]
MR --> CO[CODEOWNERS per path]
MR --> BR[Single branch protection]
The benefit is that a single commit can touch every directory it needs to. The cost is that every commit is visible to every consumer of the trunk, and the working tree contains every directory even when the engineer only works on one.
What the build system has to do
A monorepo without a build system that understands the dependency graph is a monorepo that rebuilds the whole world on every commit. The published large-scale examples all share this property:
- Google’s Piper / Bazel. Bazel is aware of the dependency graph between directories; a change to a leaf triggers only the targets that depend on it, and the build graph is the source of truth for what to run.
- Meta’s Buck and the source-control record. The build graph is keyed by directory and target; CI runs the targets the changed directory owns, and the dependency graph is cached at the source-control level.
- Microsoft’s internal build systems. The published whitepaper describes a single source tree shared across product divisions, with a build system that knows the entire dependency graph.
The pattern is the same: a monorepo needs a build graph as well as a source graph. Without the build graph, the monorepo is a CI tax. With the build graph, it is the unit of atomic cross-component change.
What the working tree has to do
Cloning a monorepo that contains a decade of history is expensive. The two mitigations are partial clone and sparse-checkout:
REPO_URL="https://github.com/example/monorepo.git"
git clone --filter=blob:none "$REPO_URL"
cd monorepo
git sparse-checkout init --cone
git sparse-checkout set "services/payments"
git ls-files | wc -l
The --filter=blob:none flag tells the server to omit blobs
from the initial fetch; Git downloads them on demand as the
checkout populates files. The sparse-checkout init --cone
mode restricts the working tree to a single directory and its
subtree; the git ls-files | wc -l line verifies that the
working tree is actually the subset, not the whole repository.
What the published examples look like
The large-scale monorepo examples are not aspirational; they are operational:
- Google. The Piper monorepo holds billions of lines, with a build graph (Bazel) that knows the dependency between every target. The stated motivation is the atomicity of cross-component refactors.
- Meta. The source-control system is a monorepo with a build system (Buck) and a CI matrix keyed by directory. The stated motivation is the visibility of a single change across the entire codebase.
- Microsoft. The Microsoft monorepo holds product source across many divisions, with a build system that understands the dependency graph. The stated motivation is the shared tooling and the shared review surface.
The trade-off published in all three is the same: the team pays the cost of the build graph and the sparse-checkout infrastructure in exchange for the atomicity of cross-component changes and the visibility of every commit.
Production discipline
- Pair every monorepo with a build graph. Without the graph, the CI matrix becomes a tax on every commit.
- Use sparse-checkout for working-tree performance, not for isolation. The isolation mechanism is CODEOWNERS plus branch protection.
- Verify the working tree with
git ls-filesafter a sparse-checkout. A working tree that contains the whole repository is a working tree that is paying the full cost. - Treat the trunk as sacred. A monorepo’s trunk is the single point of failure for the entire codebase; the branch protection on that trunk is the single point of control.
Cross-course references
- Linux for Production Sysadmins - Part XXVI (RepoLayout) covers the filesystem analogue: a single root tree versus per-service trees, and the cost of the inode walk.
- Ansible for Production Sysadmins - Part XXXVII (RepoArch) covers the role-based layout of a monorepo Ansible repository, and the alignment between roles and owner rows.
- Terraform for Production Sysadmins - Part IX (State) covers why Terraform state is the strongest argument for keeping Terraform in its own repository, even inside a monorepo.
Quiz
Knowledge check · 4 questions
Q1. A team of eighty engineers adopts a monorepo. The CI matrix rebuilds the entire repository on every commit. What is the missing piece that makes a monorepo tractable at this scale?
Q2. Sparse-checkout can be used to prevent an engineer from reading a directory they do not own.
Q3. Name the two Git features that make a monorepo's working tree tractable at scale, and the two operational mechanisms that make the *rest* of the model work.
Q4. Recommend a working-tree setup for an engineer onboarding onto a monorepo that contains 200,000 files, and verify the setup with `git ls-files`.
An engineer joins a team whose monorepo holds 200,000 files across Terraform, Ansible, Kubernetes, and application code. The engineer will only work on `services/payments/` for the next quarter. The team is concerned about clone time, working-tree size, and auditability. Recommend the working-tree setup and a verification step.
Passing score: 75%. Answers are checked in this browser.