A repository boundary is four things at once: a permission boundary, a review boundary, a CI boundary and a blast-radius boundary.
You cannot move one without moving the others, and most monorepo-versus-polyrepo arguments are really arguments about which of the four somebody is optimising. Recognising that turns an unwinnable debate into a decision.
The four boundaries
Section titled “The four boundaries”Permission. Repository access is the coarsest access control GitHub offers. Everything finer — CODEOWNERS, path rules, rulesets — is enforcement on top of a repository that somebody can already read.
Review. Who is asked, and who can meaningfully review. A reviewer who recognises nothing in a diff is a rubber stamp.
CI. What runs on a change. In one repository, everything is a candidate; across many, only that repository’s pipeline runs.
Blast radius. How much one bad change can affect. This is the one platform engineers care about and the one developers notice least.
The trade in one sentence: more repositories give smaller blast radius and cleaner permissions at the cost of coordination; fewer give atomic changes and simplicity at the cost of everything being adjacent to everything else.
The comparison
Section titled “The comparison”| Infrastructure monorepo | Polyrepo | |
|---|---|---|
| Cross-cutting change | One pull request | N, with an ordering nobody enforces |
| Blast radius | Whole estate | Per repository |
| Permission boundary | Paths, via rules | The repository |
| Consistent versions | By construction | By discipline |
| CI runtime | Needs change detection | Simple per repository |
| CI configuration | One | N, or a shared reusable workflow |
| Discovery | One place to look | Which repository? |
| Onboarding | Read one repository | Find the repositories |
| Release independence | Everything moves together | Independent |
| Shared code | A directory | A versioned artifact |
| Review quality | Degrades with breadth | Reviewers know their area |
| Tooling at scale | Needs investment | Works out of the box |
Neither column is better. Each row is a real property somebody will care about.
Scenarios
Section titled “Scenarios”The useful form of this question is not “which is better” but “what fits this situation”.
A five-person team, one cloud account, three environments. One repository. The coordination cost of splitting exceeds any benefit, everybody can review everything, and the blast radius of the estate is the estate. Teams that split at this size spend their time on cross-repository choreography they did not need.
A twenty-person team, several services, one platform team. One repository with CODEOWNERS per path, plus a separate repository for shared modules once two consumers need different versions. The single repository is still the right default; the modules leave because versioning is what they need.
Several teams owning distinct services. A repository per team’s infrastructure, plus a shared modules repository. The boundary matches ownership, which is the strongest reason to have one. Cross-cutting changes become coordination, and that coordination was already happening between the teams.
A regulated production environment. A separate production repository, with its own access list, its own rulesets and its own audit trail. Here the permission boundary is a requirement rather than a preference, and it justifies the overhead by itself.
A platform team serving many product teams. The platform’s own infrastructure in one repository, modules and templates in others, and each product team owning theirs. The platform team’s job is the interface, which is the capstone’s subject.
Many clusters or regions with different operators. A repository per cluster or per region, because the operators differ. Shared configuration lives in modules or a common base.
An estate nobody can describe. Repositories accumulated over years with no clear boundaries, some with no owner, several that nothing has changed in eighteen months. This is the most common real situation and the one least served by the abstract comparison. The answer is not a big-bang consolidation or split — it is drawing the ownership map first, finding the repositories that map to nobody, and moving things to match the map gradually. Most of the value comes from the map rather than from any subsequent restructuring.
What a monorepo needs to work
Section titled “What a monorepo needs to work”The costs are real and they are addressable.
CODEOWNERS, genuinely maintained. Paths are the only ownership boundary available, and a CODEOWNERS file that has not been updated in a year is worse than none because it routes to people who have moved on.
Change detection in CI. Planning thirty root modules on every pull request trains reviewers to skim. This is a system you build and maintain, and it is the main engineering cost of the monorepo.
A dependency map, or a crude fallback. Path-based detection misses the case where a shared module changes and everything consuming it is affected — a pull request that appears to touch nothing while changing everything. Either maintain an explicit map of consumers or plan everything whenever a shared path changes. The second is cruder, slower, and much harder to get wrong, which for infrastructure is usually the right trade.
Rulesets scoped to paths. Production directories requiring more approvals than development ones.
A navigable structure. A root README and a shallow top level, or the discovery advantage evaporates.
Discipline about what goes in. A monorepo that accepts everything — application code, infrastructure, documentation, one-off scripts, an abandoned prototype — becomes an estate nobody can hold in their head, at which point it has the coordination costs of a large repository and none of the coherence that justified it. A stated rule about what belongs, applied at review, is what prevents this.
What a polyrepo needs to work
Section titled “What a polyrepo needs to work”Equally real, and less often planned for.
A way to find things. A catalogue, a naming convention, or a README in a well-known place. “Which repository holds the payments network configuration” should not require asking.
Versioned shared code. Modules and collections with tags, release notes and an upgrade path. Without this, teams copy, and copies diverge.
Reusable CI. N repositories with N hand-maintained pipelines diverge. A reusable workflow referenced by tag is the mechanism, and it becomes a shared dependency with its own versioning question.
A story for cross-cutting changes. Upgrading a module across twelve repositories is twelve pull requests. Automating their creation is the difference between a day and a week.
Consistent conventions. Twelve repositories with twelve directory layouts is twelve things to learn. Templates help; enforcement helps more.
An answer to “who owns the repository nobody has touched in two years.” Repositories outlive teams, and an unowned one still deploys things.
The costs people underestimate
Section titled “The costs people underestimate”In a monorepo: review breadth. Reviewers stop recognising most of what they see, and approval quality falls without anybody noticing — there is no metric for it and the pull requests still get approved. CODEOWNERS addresses it, and only if the routing is accurate and maintained.
In a monorepo: blast radius of tooling. A broken CI configuration blocks everybody. A bad ruleset affects every path.
In a polyrepo: version skew. Twelve repositories on nine module versions, and nobody knows which are behind — because answering the question requires checking twelve places. This is the failure that eventually forces a large coordinated upgrade under time pressure, typically prompted by a security advisory rather than by choice.
In a polyrepo: the invisible dependency graph. Repository A’s outputs feed repository B, recorded nowhere. In a monorepo the coupling is at least in one place.
In a polyrepo: onboarding. A new engineer needs to find and clone six repositories before they can understand how anything fits together.
In both: the migration. Splitting or consolidating is a project, and doing it twice because the first attempt was wrong is the outcome to avoid by thinking about the four boundaries first.
Deciding
Section titled “Deciding”-
Draw the ownership map. Who is accountable for which parts of the estate? This is the question everything else follows from, and it is frequently the first time a team has written it down.
-
Identify hard permission requirements. Does anything need to be inaccessible to some people? Regulatory separation, a contractor with limited scope, a production environment with a restricted list. Those are repository boundaries, not path rules.
-
Count the cross-boundary changes. Over the last three months, how many changes would have spanned the proposed boundaries? If it is most of them, the boundaries are in the wrong place.
-
Check whether shared code needs independent versions. If two consumers need different versions of the same module, it leaves — regardless of everything else.
-
Measure your CI pain honestly. Slow pipelines are usually solvable with change detection before they justify a split.
-
Default to fewer repositories and split when a specific force requires it. Splitting is easy; consolidating is not.
-
Write down the decision and why. A decision record ends the annual re-litigation.
Step 1 is the whole exercise. A repository structure that does not match the ownership map produces friction in every direction, and no amount of tooling fixes it.
The hybrid most estates actually have
Section titled “The hybrid most estates actually have”Presenting this as a binary is a simplification. Real organisations sit somewhere in the middle, deliberately or otherwise.
The common shape: one infrastructure repository per team or domain, one or two shared module repositories, and a separate production configuration repository where the permission boundary is required. That is a polyrepo with monorepo-shaped pieces, and it is a reasonable place to land.
The other common shape: an infrastructure monorepo containing everything the platform team owns, alongside per-service repositories where application teams keep their own deployment configuration. Two models coexisting because two groups have different needs.
Both are fine. What is not fine is arriving there without noticing — a structure that emerged from a series of individually reasonable decisions and that nobody could now justify as a whole.
The question to ask about a hybrid: can you state the rule for what goes where? “Shared modules in the modules repository, environment configuration in the config repository, application deployment configuration with the application” is a rule somebody can follow. “It depends who created it” is not.
Write the rule down. A paragraph in a platform README saying where new things go prevents the next five decisions from being made ad hoc, and it is the cheapest possible governance.
Scale changes the answer
Section titled “Scale changes the answer”The structure that works for four engineers does not work for forty, and the transition is gradual enough to miss.
Under about ten contributors, one repository is almost always right. Everybody talks to everybody, review works, and discovery is trivial.
Between ten and thirty, the pressures appear: CI time, review breadth, and the first genuine ownership disputes. The fixes are CODEOWNERS and change detection, not splitting.
Above thirty, boundaries that match teams become necessary because a repository forty people change is a repository nobody feels responsible for.
At platform scale — hundreds of engineers, many teams — the question inverts. The platform team stops deciding how to structure one estate and starts deciding what structure to offer, as templates other teams instantiate.
The signal that you have crossed a threshold is not the repository’s size. It is people waiting: for a review, for a pipeline, for somebody else’s change to merge. Waiting is the cost that grows superlinearly, and it is what a boundary buys relief from.
Migrating
Section titled “Migrating”Both directions, and neither is a weekend.
Splitting a monorepo:
Extract one component at a time, starting with the one with the clearest boundary. Preserve history where it matters — git subtree split or a filter tool. Set up the new repository’s CI before moving anything into it. Move, verify the pipeline produces the same result, then remove the original. Update every reference — module sources, workflow references, documentation, runbooks. Expect the references to be the long tail.
Consolidating a polyrepo:
Bring repositories in as subdirectories, one at a time. Merge histories with a subtree merge if the history matters. The CI configuration is the hard part: N pipelines becoming one that handles every case is more work than it looks, and it is best done incrementally with the old pipelines still running until each is replaced.
In both cases: do not change behaviour during the move. A restructure that is a no-op is verifiable; one mixed with functional changes is not.
And in both cases: the references are the long tail. Module sources, documentation, bookmarks, CI configuration in other repositories, and the runbook nobody has opened in a year.
Tooling that changes the calculus
Section titled “Tooling that changes the calculus”Some of the classic monorepo costs are addressable, and knowing which changes the decision.
Change detection turns “every pull request plans everything” into “every pull request plans what it affects”. This is the single most valuable investment for a monorepo and it removes the most-cited objection.
Sparse checkout and partial clone make a large repository usable without fetching all of it, which addresses the clone-time objection that used to be decisive.
Path-scoped rulesets let one repository have different rules for different directories — production requiring two approvals while development requires none. This closes much of the gap between path rules and repository boundaries, though not the read-access part.
Reusable workflows address the polyrepo’s CI duplication, turning N pipelines into N three-line files calling one definition.
Automated cross-repository pull requests address the polyrepo’s upgrade coordination — a bot opening the same module bump in twelve repositories is a very different proposition from doing it by hand.
What no tooling fixes:
Read access. A repository is either readable or not. Path rules control who must approve, not who can see.
Review comprehension. Nothing makes a reviewer understand code they do not work on.
The blast radius of a bad ruleset or workflow in a monorepo.
The invisible dependency graph in a polyrepo.
The practical consequence: most classic monorepo objections are solvable with engineering, and the permission one is not. If your reason for splitting is CI time or clone size, build the tooling. If it is who can read production configuration, split.
Common mistakes
Section titled “Common mistakes”Copying a large company’s structure. It solves coordination problems you do not have.
Splitting before the ownership map exists. Boundaries in arbitrary places.
A monorepo with no CODEOWNERS. The only available ownership boundary, unused.
A polyrepo with no shared-code versioning. Teams copy, copies diverge.
Splitting to fix slow CI. Change detection is cheaper.
Consolidating to fix inconsistency. Templates and shared workflows are cheaper.
Ignoring hard permission requirements. Path rules are not an access boundary.
Migrating and changing behaviour together. Nothing is verifiable.
Assuming the decision is permanent. Estates change; revisit annually.
What GitOps adds to the question
Section titled “What GitOps adds to the question”If a reconciler is watching the repository, the boundary decision acquires an extra dimension.
A controller’s scope should match a repository boundary, or a clear path within one. A controller watching a repository root that contains configuration for other clusters will apply it — which is the sharpest version of the blast-radius argument.
Cluster credentials follow the repository. With one config repository watched by every cluster’s controller, every cluster has read access to every cluster’s configuration. That is usually acceptable and it is worth knowing.
A push to a shared repository triggers every watching controller. Thirty clusters reconciling on every merge is load and a coordination point, and it means one bad commit reaches everything simultaneously. Staged rollout — clusters pinned to different tags with deliberate promotion — is the counter, and it is a design rather than a default.
Write access to the watched branch is deployment access. Whoever can merge can change the cluster. That makes branch protection on a GitOps config repository a security control rather than a process preference, and it is an argument for a separate config repository where the application repository’s write list is broad.
The practical upshot: GitOps pushes toward a separate configuration repository with a tighter access list, even where the rest of the estate is a monorepo. That is one of the more common and better-justified hybrid arrangements. Kubernetes repository structure works through the layouts.
Revisiting the decision
Section titled “Revisiting the decision”Repository structure is not permanent, and the useful cadence for reconsidering it is annual rather than never or constantly.
The questions worth asking each year:
Has the ownership map changed? Teams reorganise, services move, people leave. A structure that matched the organisation two years ago may not now.
Is anybody waiting? For reviews, for pipelines, for somebody else’s merge. Waiting is the cost that justifies a boundary.
Has the estate grown past what the structure supports? A monorepo that has doubled may now need change detection it did not need before.
Is version skew appearing? In a polyrepo, this is the signal that shared code needs better tooling or fewer repositories.
Are there repositories nobody owns? In a polyrepo, they accumulate.
What would a new joiner say? Somebody who arrived last month has the clearest view of whether the structure is navigable, and they will stop noticing within a quarter.
What not to do: restructure because a conference talk described something different. The costs of a migration are real and the benefits of somebody else’s structure are speculative for you.
What to do instead: fix the specific friction. Most structural complaints resolve to a missing CODEOWNERS entry, a slow pipeline, or an undocumented convention — all cheaper to fix than a migration.
Mental model
Section titled “Mental model”A repository boundary is a permission boundary, a review boundary, a CI boundary and a blast-radius boundary — simultaneously and inseparably. Put boundaries where your ownership map has them, and accept the coordination cost that follows.
The corollary that settles most arguments: if two people disagree about this, they are usually optimising different boundaries. Ask which one each of them means, and the disagreement usually resolves into a real trade-off you can decide.
What you learned
Section titled “What you learned”- A repository boundary is four boundaries at once and they cannot be separated
- The ownership map is the input; the structure should follow it
- Monorepos need
CODEOWNERS, change detection and a dependency story - Polyrepos need discovery, versioned shared code, reusable CI and a cross-cutting change story
- Hard permission requirements are the one force that unambiguously requires separate repositories
- Slow CI is a change-detection problem before it is a structural one
- Default to fewer repositories: splitting is easy, consolidating is not
- Never change behaviour during a migration
Exercise
Section titled “Exercise”Use your own organisation’s repositories, or a hypothetical estate.
-
Write the ownership map: every part of the infrastructure, and who is accountable.
-
Compare it to the current repository structure. Predict: how many mismatches?
-
Take the last twenty infrastructure pull requests. How many would have spanned a proposed boundary?
-
List the shared code. For each, do two consumers need different versions today?
-
Identify any hard permission requirement. Is it currently enforced by a repository boundary or by a path rule?
-
Measure the CI runtime for a typical change. How much of what ran was relevant?
-
Write the decision record — the structure you would choose and why. Include what would make you revisit it.
Related lessons
Section titled “Related lessons”The GitOps and infrastructure repository templates are in the Professional Toolkit.