DEV Community

Cover image for Monorepo vs Polyrepo: What Actually Matters When You’re Building at Scale
Parth Sarthi Sharma
Parth Sarthi Sharma

Posted on

Monorepo vs Polyrepo: What Actually Matters When You’re Building at Scale

The repository strategy is not really a Git decision. It is an architecture, ownership, and team-boundary decision.

When a team starts building multiple services, one question usually appears sooner or later:

Should we keep everything in a monorepo, or should every service have its own repository?

The discussion often turns into a familiar list:

  • Monorepo = easier collaboration
  • Polyrepo = better isolation
  • Monorepo = easier refactoring
  • Polyrepo = better team autonomy

All of these can be true.

And yet, they don't answer the question that actually matters:

Which repository structure will make our engineers more effective as the system and organisation grow?

After working with distributed systems, microservices, shared libraries, CI/CD pipelines and multiple engineering teams, I've found that the repository itself is rarely the root problem.

The more important questions are:

  • Who owns the code?
  • How often do services change together?
  • How tightly coupled are the teams?
  • How are dependencies managed?
  • How expensive is CI/CD?
  • How easy is cross-service refactoring?
  • Where do security and access boundaries exist?
  • How much autonomy do teams actually need?

This article looks at monorepo vs polyrepo from that perspective.


1. First: What Are We Actually Deciding?

At first glance, the decision seems simple.

Monorepo

company/
├── services/
│   ├── orders/
│   ├── payments/
│   ├── customers/
│   └── notifications/
├── libraries/
│   ├── auth/
│   ├── logging/
│   └── common/
├── frontend/
└── infrastructure/
Enter fullscreen mode Exit fullscreen mode

One repository contains multiple applications, services and shared components.

Polyrepo

orders-service       → repository
payments-service     → repository
customers-service    → repository
notifications        → repository
common-library       → repository
frontend             → repository
infrastructure       → repository
Enter fullscreen mode Exit fullscreen mode

But Git repository boundaries don't necessarily equal architectural boundaries.

A system can have:

  • 50 repositories
  • 20 deployable services
  • 5 teams
  • shared libraries
  • tightly coupled release processes

and still effectively behave like one system.

Likewise, a monorepo can contain completely independent applications owned by different teams.

So the real decision is not:

"How many Git repositories should we have?"

It is:

"Where should we place boundaries between teams, code, dependencies and delivery pipelines?"

That's a much more useful question.


2. Monorepo: What It Gives You

A monorepo provides one major capability that becomes extremely valuable as systems evolve:

You can see and change the system as one codebase.

Imagine we have:

orders-service
payments-service
customer-service
Enter fullscreen mode Exit fullscreen mode

and we need to change an API contract.

In a monorepo:

Change API contract
       ↓
Update producer
       ↓
Update consumers
       ↓
Compile/test everything affected
       ↓
Review one change
       ↓
Merge
Enter fullscreen mode Exit fullscreen mode

The compiler and CI system can help us identify the blast radius.

This becomes particularly valuable for:

  • API contract changes
  • shared libraries
  • security changes
  • large refactoring
  • framework upgrades
  • dependency upgrades
  • consistent engineering standards

For example:

Java 17 → Java 21

        ↓

orders
payments
customers
notifications
reporting
Enter fullscreen mode Exit fullscreen mode

A monorepo makes it much easier to discover what needs to change.


3. The Hidden Superpower: Refactoring

This is one of the biggest practical advantages of a monorepo.

Suppose we have:

CustomerService.getCustomer()
Enter fullscreen mode Exit fullscreen mode

used by:

Orders
Payments
Invoices
Reporting
Notifications
Enter fullscreen mode Exit fullscreen mode

Now imagine changing:

getCustomer()
Enter fullscreen mode Exit fullscreen mode

to:

getCustomer(CustomerId id)
Enter fullscreen mode Exit fullscreen mode

In a monorepo, the IDE/compiler/test system can often identify the impact immediately.

With multiple repositories, you may need to:

  1. Change the shared library
  2. Publish a new version
  3. Update repository A
  4. Update repository B
  5. Update repository C
  6. Wait for their CI pipelines
  7. Deal with version compatibility
  8. Eventually remove the old API

The technical change may be tiny.

The organisational change can be much larger.


4. But Monorepos Are Not Free

A monorepo can become painful if we don't invest in engineering tooling.

Imagine this:

monorepo
│
├── 100 services
├── 20 libraries
├── 10 frontend applications
└── millions of lines of code
Enter fullscreen mode Exit fullscreen mode

A developer changes one line in:

payments-service
Enter fullscreen mode Exit fullscreen mode

and CI responds with:

Build everything
Test everything
Deploy everything
Enter fullscreen mode Exit fullscreen mode

Now the repository becomes the bottleneck.

This is where people sometimes incorrectly conclude:

"Monorepos don't scale."

The more accurate conclusion is:

Naive monorepo tooling doesn't scale.

A serious monorepo needs:

  • dependency graphs
  • incremental builds
  • affected-test detection
  • remote build caching
  • parallel CI
  • clear ownership
  • CODEOWNERS
  • good developer tooling

The goal should be:

Developer changes payments
          ↓
Dependency graph
          ↓
Identify affected components
          ↓
Build/test only what matters
Enter fullscreen mode Exit fullscreen mode

Not:

Developer changes payments
          ↓
Build the entire company
Enter fullscreen mode Exit fullscreen mode

5. Polyrepo: Why Teams Choose It

Polyrepo usually starts with a very reasonable requirement:

"This team should be able to own and deploy its service independently."

For example:

Team A
 └── orders-service

Team B
 └── payments-service

Team C
 └── notifications-service
Enter fullscreen mode Exit fullscreen mode

Each team gets:

  • its own repository
  • its own CI pipeline
  • its own deployment lifecycle
  • its own permissions
  • its own release cadence

This can work extremely well.

Especially when services have genuinely independent ownership.


6. The Hidden Cost of Polyrepo

The problem appears when repositories are technically separate but operationally coupled.

Imagine:

orders-service
       ↓
payments-service
       ↓
customer-service
       ↓
shared-library
Enter fullscreen mode Exit fullscreen mode

Now a breaking change happens in the shared library.

The change becomes:

shared-library v2
       ↓
orders → update
payments → update
customer → update
reporting → update
notifications → update
Enter fullscreen mode Exit fullscreen mode

Suddenly we have a distributed coordination problem.

The repositories are separate.

The architecture isn't.

This can lead to:

  • dependency version drift
  • duplicated code
  • inconsistent frameworks
  • inconsistent security fixes
  • incompatible API versions
  • difficult cross-repository refactoring
  • pull-request coordination across teams

7. One of the Biggest Lessons: Microservices ≠ Polyrepo

This is worth calling out explicitly.

A common architecture evolution looks like this:

Monolith
   ↓
Microservices
   ↓
One repository per microservice
Enter fullscreen mode Exit fullscreen mode

The last step is not mandatory.

You can absolutely have:

                 Monorepo
                    │
       ┌────────────┼────────────┐
       ↓            ↓            ↓
    Orders       Payments     Customers
       │            │            │
       └────── independently ────┘
              deployable
Enter fullscreen mode Exit fullscreen mode

The deployment boundary and repository boundary can be different.

That's an important architectural distinction.


8. Real Engineering Scenario: Shared Libraries

Let's look at a common example.

Suppose we have:

common-auth
common-logging
common-observability
common-http
Enter fullscreen mode Exit fullscreen mode

In a polyrepo architecture, these often become separate repositories.

Then teams consume versions:

common-auth: 1.4.2
common-logging: 3.1.0
common-http: 2.7.1
Enter fullscreen mode Exit fullscreen mode

Over time:

Service A → auth 1.4
Service B → auth 1.5
Service C → auth 1.3
Service D → auth 1.2
Enter fullscreen mode Exit fullscreen mode

Now the organisation has effectively created multiple versions of the same engineering standards.

This isn't automatically wrong.

Sometimes independent versioning is exactly what we want.

But we should recognise the trade-off.


9. A Useful Rule for Shared Libraries

Ask:

Does this library represent a reusable capability or an organisational coupling mechanism?

For example:

Good candidate

Observability SDK
Enter fullscreen mode Exit fullscreen mode

It may evolve independently.

Potentially problematic

CompanyBusinessLogicUtils
Enter fullscreen mode Exit fullscreen mode

This can become a dumping ground for code that should actually belong to a domain.

A common anti-pattern is:

common/
├── StringUtils
├── DateUtils
├── CustomerUtils
├── PaymentUtils
├── OrderUtils
├── ValidationUtils
└── EverythingElseUtils
Enter fullscreen mode Exit fullscreen mode

Eventually every service depends on common.

Congratulations.

You've created a distributed monolith with a shared library.


10. Repository Boundaries Should Follow Ownership

One of the strongest signals for repository boundaries is team ownership.

Imagine:

Team: Payments

Owns:
├── payments-api
├── payment-processing
├── reconciliation
└── payment-risk
Enter fullscreen mode Exit fullscreen mode

These components may benefit from being in the same repository.

Because the same team:

  • changes them together
  • reviews them
  • deploys them
  • understands their dependencies

Now imagine:

Payments team
        ↓
Customer team
        ↓
Identity team
Enter fullscreen mode Exit fullscreen mode

If every change requires three teams to coordinate, the problem may not be the repository.

It may be the architecture.


11. Repository Boundaries Should Follow Change Patterns

This is one of the questions I now ask when evaluating repository structure:

What code tends to change together?

If these always change together:

orders
orders-api
orders-domain
orders-tests
Enter fullscreen mode Exit fullscreen mode

keeping them together makes sense.

If these change independently:

marketing-website
payment-processing
internal-data-platform
Enter fullscreen mode Exit fullscreen mode

there is little value in forcing them into one repository purely for consistency.

A useful mental model is:

High change coupling
        ↓
Keep closer together

Low change coupling
        ↓
Separate when useful
Enter fullscreen mode Exit fullscreen mode

12. CI/CD Changes the Equation

Repository strategy and CI/CD strategy are tightly connected.

A monorepo without good CI/CD becomes painful.

A polyrepo without standardised CI/CD also becomes painful.

Imagine 80 repositories.

If every repository has:

Jenkinsfile
Dockerfile
Terraform
security-scan.yml
deployment.yml
Enter fullscreen mode Exit fullscreen mode

and every team maintains its own version, you eventually get:

Repo A → Java 17
Repo B → Java 21
Repo C → old security scanner
Repo D → custom deployment
Repo E → completely different pipeline
Enter fullscreen mode Exit fullscreen mode

The repository structure isn't necessarily the problem.

Lack of platform engineering is.

A mature organisation should provide reusable delivery capabilities.

For example:

             Platform Engineering
                    │
        ┌───────────┼───────────┐
        ↓           ↓           ↓
      Build       Security    Deploy
        │           │           │
        └───────────┼───────────┘
                    ↓
              Product Teams
Enter fullscreen mode Exit fullscreen mode

Teams should be able to own their applications without having to reinvent the delivery platform.


13. Developer Experience Matters More Than We Think

Ask a developer:

"How long does it take you to make a change across three services?"

That's often more useful than asking:

"Do we use a monorepo?"

Consider the workflow.

Monorepo

Clone
 ↓
Find services
 ↓
Make change
 ↓
Run affected tests
 ↓
Single PR
Enter fullscreen mode Exit fullscreen mode

Polyrepo

Clone repo A
 ↓
Clone repo B
 ↓
Clone repo C
 ↓
Change shared contract
 ↓
PR A
 ↓
PR B
 ↓
PR C
 ↓
Version library
 ↓
Update dependencies
 ↓
Wait for pipelines
Enter fullscreen mode Exit fullscreen mode

Neither is inherently better.

The important question is:

Which workflow reflects how your engineers actually work?


14. Security and Compliance Can Change the Decision

There are cases where separate repositories make practical sense.

For example:

Public product
        │
        ├── repository A

Highly restricted system
        │
        └── repository B
Enter fullscreen mode Exit fullscreen mode

Different access requirements can be a legitimate reason for separation.

Other examples include:

  • regulated workloads
  • sensitive infrastructure
  • different organisational ownership
  • acquisition boundaries
  • external/open-source projects
  • different security classifications

This is where repository boundaries can become more than a developer-experience decision.


15. The Hybrid Model

In larger organisations, the answer doesn't have to be:

Everything → monorepo
Enter fullscreen mode Exit fullscreen mode

or:

Everything → polyrepo
Enter fullscreen mode Exit fullscreen mode

A hybrid approach can be very practical.

For example:

                    Engineering Organisation
                              │
          ┌───────────────────┼───────────────────┐
          ↓                   ↓                   ↓
     Product Monorepo     Platform Repo      Data Platform
          │                   │                   │
     ┌────┼────┐              │              ┌────┼────┐
     ↓    ↓    ↓              ↓              ↓    ↓    ↓
   API  Worker UI        Infrastructure     ETL  ML  Analytics
Enter fullscreen mode Exit fullscreen mode

The key is that each boundary has a reason.

Not:

"We have always done it this way."


16. A Practical Decision Framework

Instead of asking:

"Which is better?"

I prefer asking these questions.

Question Signal
Do multiple teams frequently modify the same code? Monorepo
Do services have genuinely independent ownership? Polyrepo
Are cross-service refactors common? Monorepo
Do systems have different security/access boundaries? Polyrepo
Do components share many libraries? Monorepo can simplify this
Do teams have very different release lifecycles? Polyrepo
Can CI efficiently build only affected components? Makes monorepo more viable
Do teams require strong repository-level isolation? Polyrepo
Are services actually independent despite being in one repo? Monorepo can still work
Are many repos creating duplicated tooling? Consider consolidation/platform tooling

The important thing is that these are signals, not absolute rules.


17. What I Would Avoid

Anti-pattern 1: "Microservices means one repo per service"

Not necessarily.

Repository boundaries and service boundaries solve different problems.


Anti-pattern 2: "Monorepo means one giant application"

Also not necessarily.

A monorepo can contain independently deployable services.

                 Monorepo
                    │
       ┌────────────┼────────────┐
       ↓            ↓            ↓
   Service A    Service B    Service C
       │            │            │
       ↓            ↓            ↓
   Deploy A     Deploy B     Deploy C
Enter fullscreen mode Exit fullscreen mode

Anti-pattern 3: "Every shared class belongs in common"

This usually creates coupling.

Before creating a shared library, ask:

Is this genuinely reusable infrastructure, or are we hiding domain ownership problems?


Anti-pattern 4: "Every repository needs its own pipeline"

Standardise the platform.

Teams should own their delivery outcomes without necessarily owning every line of CI/CD implementation.


Anti-pattern 5: "Repository count is an architecture metric"

It isn't.

I'd rather know:

  • deployment frequency
  • lead time for change
  • change failure rate
  • dependency coupling
  • build time
  • test time
  • ownership clarity
  • developer productivity

than simply:

"We have 47 repositories."


18. What I'd Do on a New Project

If I were starting a new product today, I wouldn't begin with:

"Let's choose monorepo."

or:

"Let's choose polyrepo."

I'd start with:

Step 1 — Identify teams

Who owns what?
Enter fullscreen mode Exit fullscreen mode

Step 2 — Identify deployment boundaries

What needs independent deployment?
Enter fullscreen mode Exit fullscreen mode

Step 3 — Identify change coupling

What changes together?
Enter fullscreen mode Exit fullscreen mode

Step 4 — Identify security boundaries

What requires separate access?
Enter fullscreen mode Exit fullscreen mode

Step 5 — Understand developer workflows

How will engineers build and test changes locally?
Enter fullscreen mode Exit fullscreen mode

Step 6 — Design CI/CD accordingly

Can we build/test only what changed?
Enter fullscreen mode Exit fullscreen mode

Step 7 — Choose repository boundaries

Only now would I decide:

monorepo
        OR
polyrepo
        OR
hybrid
Enter fullscreen mode Exit fullscreen mode

The repository strategy becomes an outcome of the architecture rather than the starting point.


19. My Rule of Thumb

If I had to reduce the whole discussion to one principle:

Put things together when they need to change together. Separate things when they need to be owned, secured, or released independently.

And there is an important second principle:

Don't use repository boundaries to solve problems that actually belong to architecture, ownership, or platform engineering.

A monorepo won't fix poor service boundaries.

A polyrepo won't create team autonomy by itself.

Microservices won't automatically give independent deployment.

And multiple Git repositories won't automatically create a scalable engineering organisation.


20. The Takeaways

If you're deciding between monorepo and polyrepo, I'd keep these points in mind:

1. Repository boundaries are architectural boundaries

Treat them deliberately.

2. Microservices do not require polyrepos

Services can be independently deployable from a monorepo.

3. Monorepos need good tooling

Incremental builds, dependency graphs, caching and affected-test execution become important at scale.

4. Polyrepos need platform engineering

Otherwise every team ends up maintaining its own CI/CD, security and developer tooling.

5. Shared libraries can create hidden coupling

Especially when they contain business logic.

6. Team ownership matters

Repository boundaries should make ownership clearer, not more complicated.

7. Look at change patterns

Ask what actually changes together.

8. Optimise for developer flow

The best architecture is often the one that reduces unnecessary coordination while preserving the boundaries the organisation genuinely needs.


Final Thought

The monorepo vs polyrepo debate is often presented as if there is a universally correct answer.

There isn't.

The better question is:

"What repository structure allows our teams to change the system safely, independently and efficiently?"

Once you start looking at the problem through:

ownership → coupling → change patterns → security → deployment → developer experience

the decision becomes much less ideological and much more engineering-driven.

And that's probably the most important lesson:

Choose repository boundaries based on how your organisation and software actually change — not based on what architecture is fashionable today.

Top comments (0)