What Is a Monorepo? Pros and Cons of Managing Many Projects in One Repo

What is a monorepo?
A monorepo is a single version control repository that holds several related applications and libraries. In practice, each project lives in its own folder, yet all of them share one history, one change flow and usually one set of tool settings. In short, it is one repo for many projects.
The key point is that a monorepo is a repository choice, not an architecture. So projects inside it can ship independently. In other words, living in one repo does not mean being one package.
First, in this guide we answer the question at a conceptual level. We skip command and configuration details, because our goal is to help you make the decision with confidence.
Example scenario: a team builds a website, a mobile app and a payment library that both of them use. For example, with three repos, every change to the library needs coordination in three places. Instead, with one repo, the same change lands in a single step.
What is a monorepo in plain words, with a simple analogy?
First, picture an apartment building. Each flat has its own door and its own layout, but the entrance, the elevator and the parking lot are shared. So when someone replaces the elevator, every flat gets the new one at the same time.
A polyrepo is more like a street of detached houses. Also, each house has its own garden, its own repairs and its own rules. You get plenty of freedom, but changing something shared means knocking on every door.
Of course, the analogy has limits. In a building, however, everyone follows one management board. In software, you define ownership and access rules yourself, so a monorepo does not have to open everything to everyone.
Is a monorepo the same as a monolith?
No, and people mix the two up often. First, a monolith describes an application that runs as one piece. Second, a monorepo describes where the code lives.
For example, a monorepo can hold three services, a web interface and a shared library. In practice, you deploy and scale each of them separately. On the other hand, you can split a monolith across several repositories if you want to.
- Monorepo: how you store code (a repository-level decision).
- Monolith: how the application runs (an architecture-level decision).
- Microservices: splitting the application into small independent services (an architecture-level decision).
So the thought "we are moving to microservices, therefore we need many repos" is wrong. Instead, many teams keep most of their microservices in one repo. You make the two decisions independently.
How does a monorepo work?
In practice, a typical monorepo has a few top-level folders. One folder holds the applications, another holds the shared packages, and a third holds common configuration. Each project keeps its own dependency definition, while the root of the repo ties them together.
However, what matters is that your tooling understands the links between projects. If an app uses a shared library, the tool reads that relationship and builds a dependency graph. Therefore, this graph is the foundation of the smart builds we describe below.
From a Git point of view nothing changes. In other words, you still have one history, one main branch and one change flow. Basic Git knowledge is therefore a good starting point, and our Git and GitHub guide covers it if you want a refresher.
How do you manage dependencies inside a monorepo?
Dependency management is the quiet hero of every monorepo. There are two kinds: internal dependencies between your projects, and external dependencies on third-party libraries. If you ignore either one, the repo drifts into chaos.
For internal dependencies, however, the rule is simple. A project should depend only on shared packages below it. Apps use packages, but packages never depend on apps. As a result, this direction rule prevents circular dependencies and makes it easier to find the affected projects.
For external libraries, we also see two approaches. The first is a single version policy, where all projects use the same version. The second is a flexible setup, where each project picks its own version. A single version policy gives consistency; however, it forces you to upgrade everywhere at once.
- Write the dependency direction in one sentence: apps depend on packages, never the other way around.
- Pick a single version policy or a flexible one, then document it.
- Remove unused dependencies at regular intervals.
When you draw package boundaries, our SOLID principles article is a good compass. Then if every package has one responsibility, the dependency graph stays simple.
What is a monorepo compared with a polyrepo?
A polyrepo keeps every project or package in its own repository. Therefore, placing the two approaches side by side makes the decision easier. The table below summarizes general tendencies, and your real results depend on your team and tools.
| Topic | Monorepo | Polyrepo |
|---|---|---|
| Code sharing | You use shared code directly in the same repo | You publish packages and consume them by version |
| Change touching many projects | One change and one review | A separate change and review in each repo |
| Tool and setting consistency | Easy to keep shared configuration | Each repo can drift over time |
| Build and test time | Slows down as it grows without smart tooling | Starts fast because each repo stays small |
| Access and permissions | Needs folder-level rules | Natural boundary at the repo level |
| Repository size | Grows over time, may need Git tuning | Stays small per repo |
| Independent release rhythm | Possible, but needs extra tooling | Possible by default |
| New developer onboarding | One clone shows everything | They must find the right repo first |
What is a monorepo good for, then? Shared code and consistency. A polyrepo is good for hard boundaries and independence. Both can be right, and the real question is which one cures the pain your team actually feels.
What are the advantages of a monorepo?
All the advantages come from one root: because everything lives in one place, visibility goes up. Here are the benefits teams feel most in the field.
- Shared code: You reuse interface components, validation rules and type definitions without copying them.
- Atomic changes: You update a library and every app that uses it in a single change.
- Consistent tooling: Formatting, linting and test settings live in one place.
- Easy discovery: You can search the whole repo to see where a function is used.
- Simpler dependency management: The question of which version of a shared library each app runs mostly disappears.
- Easier onboarding: A new developer clones one repo and sees the whole system.
In practice, these benefits show up most when projects are tightly connected. For truly independent projects, however, the gain stays small.
There is also a human side. A developer in a single repo can read another team's code and learn from it. Walls between teams get lower, and questions become easier to ask. However, this transparency also needs shared rules, otherwise everyone touches everything.
Why is an atomic change so valuable in a monorepo?
An atomic change means that related pieces change together in one operation. For example, imagine you rename a function in a shared library, and three apps use that function.
In a polyrepo, you first change the library and publish a new version. Then you bump the version in three repos. Meanwhile, the system can stay inconsistent. On top of that, you must track which app is stuck on which version.
Instead, in a monorepo, you update the library and the three apps in the same change. Then the reviewer sees the whole impact on one screen. The tests also run for all of them together, so you catch a broken link before the merge.
In short, this is the strongest argument of monorepo supporters. Still, the benefit has a price: as changes grow, the review load grows too. A habit of small, focused changes becomes critical.
What are the challenges and risks of a monorepo?
Putting everything in one place also collects the problems in one place. So these are the challenges teams meet most often.
- Build and test time: A setup that builds every project on every change slows down badly as the repo grows.
- Permissions and access: If everyone can write to every folder, responsibility gets blurry.
- Repository size: As history and file count grow, cloning and some Git operations get heavier.
- Tool dependence: Large repos usually need specialized build tools, and those tools have a learning curve.
- Breaking change risk: A mistake in a shared package can hit many apps at the same moment.
- Release confusion: If you do not set clear rules, nobody knows which project ships when.
Each item on this list has a remedy, so we cover them below. Starting without any remedy is still the most common mistake in monorepo projects.
For example, a team moves to one repo and a few months later notices that every change runs the whole test suite. Then the wait grows and developer patience runs out. In other words, the problem is not the monorepo itself, it is the missing tooling.
What does the affected build concept mean?
An affected build means building and testing only the projects a change touches, plus the projects that depend on them. First, the tool maps the changed files onto the dependency graph. As a result, unrelated projects get skipped.
Example scenario: the repo has an admin panel, a mobile interface and a payment library. First, you edit a text in the admin panel. The mobile interface and the payment library do not depend on that change, so you do not need to rebuild them.
If you change the payment library instead, the picture flips. Every project that depends on the library counts as affected, and you test all of them. In practice, this distinction is the core mechanism that keeps a large monorepo fast.
The Nx documentation explains that the tool builds a project graph from your codebase and uses it to find affected projects. Other tools have their own methods, but the logic stays the same, so the concept transfers.
How does a build cache work inside a monorepo?
A build cache stores the result of a task and reuses it when the same input appears again. First, the tool computes a fingerprint from the source files and the configuration. If it sees the same fingerprint, it returns the stored result instead of repeating the work.
In practice, this means you do not rebuild a package that a colleague built yesterday. Also, if the cache is shared (a remote cache), you can reuse the result your teammate produced.
The Bazel introduction describes how the tool caches work it has already done and rebuilds only what it must. However, for the cache to be reliable, you have to declare every input completely. Otherwise, a missing declaration produces a wrong result.
How does continuous integration work in a monorepo?
Continuous integration means running builds and tests automatically on every change. In one repo, therefore, this flow has to calculate the impact of each change. Otherwise, a tiny fix triggers the entire test suite.
In a good setup, the flow runs in three steps. First, you find the changed files. Then you use the dependency graph to find the affected projects. Finally, you run build and test only for those.
This is the second pillar that keeps a monorepo fast. In practice, combined with the cache, only a small subset really runs for most changes. Even so, a change to a big shared package always triggers a wide test round, and that is expected.
Another detail is readable output. In a repo with hundreds of projects, you also need to see clearly which project broke and why. So spend some time on the output layout while you build the flow.
How do you manage releases in a monorepo?
However, living in the same repo does not mean shipping at the same time. In practice, there are two basic models. First, in the fixed version model, all packages carry the same version number. Second, in the independent version model, each package moves at its own pace.
What is a monorepo release model in practice? Most small teams pick the fixed model, because it is simple. The independent model is more flexible, yet it needs a setup that tracks which change bumps which package. Also, release tools that keep a change log help here.
- First, split your packages into two groups: apps that reach users and libraries used only inside.
- Define the release flow for each app separately, so each app can deploy to its own environment.
- Write the version rule for published packages and share it with the team.
- Rehearse the rollback plan for every release flow in advance.
If you deploy your apps as containers, our Docker and containers guide helps you set up that flow. In practice, a monorepo does not stop each app from building its own image.
In short, a monorepo does not take away your release independence. It only asks you to define that independence yourself.
What are the main monorepo tool categories?
First, we do not push a single product here. Options change fast, so thinking in categories is more durable. We suggest checking current features in each tool's own official documentation.
| Category | What it does | Example concepts |
|---|---|---|
| Workspace management | Links many packages in one repo and shares dependencies | Workspace features of package managers |
| Task runner | Orders tasks across projects, finds affected projects, caches results | Project graph, affected commands, task cache |
| Build system | Gives detailed, repeatable builds for multi-language and very large repos | Targets, dependency graph, remote cache |
| Versioning and release tool | Coordinates package versions and publishing | Change logs, version bump rules |
| Code ownership | Assigns review responsibility per folder | Ownership file, required review |
In the JavaScript ecosystem, npm workspaces are the simplest example of the first category. They come with the package manager, so no extra tool is needed. Among task runners, people mention Nx and Turborepo. Among build systems, they mention Bazel and Pants. Therefore, the right choice depends on your language and repo size.
How does Git stay fast in a very large monorepo?
When a repo grows, you do not always need every file in your working directory. Instead, Git offers partial working tree features for this. The Git sparse-checkout documentation explains that you can reduce your working tree to a subset of the tracked files.
In practice, you keep only the folders you work on on your disk. As a result, even if the repo is huge, your daily work stays small. Also, according to the documentation, cone mode lets you list only directories, which keeps the setup simple.
Git also offers cloning options that skip part of the history. For exact commands and flags, check the Git documentation, because details change between versions. In small and mid-sized repos, however, you do not need these settings.
How do you manage permissions and code ownership in a monorepo?
One repo does not mean everyone writes everywhere. Instead, you define responsibility at the folder level. Platforms such as GitHub offer a code owners file for this purpose.
According to the GitHub documentation, the platform automatically requests a review from the code owners when a pull request changes their code. So every change that touches the payment folder passes through the payment team.
- Name a responsible team for every top-level folder.
- Add the ownership file to the repo and map it to team names.
- Turn on the required review rule for the main branch.
- Give shared packages more than one owner, so nobody becomes a single point of failure.
- Update the ownership list whenever teams change.
As a result, with this setup, a monorepo offers shared visibility and clear responsibility at the same time.
When should you choose a monorepo?
In practice, a monorepo creates the most value when your projects touch each other often. If several of the signs below sound familiar, evaluate the approach seriously.
- The same team builds several apps and a shared library together.
- The team copies a shared interface component or type definition into many projects.
- You argue about version mismatches and "it works on my machine".
- Coordinating a change across repos takes longer than the change itself.
- Differences in formatting, linting and test settings between repos cause trouble.
Example scenario: an e-commerce team builds a storefront, an admin panel and a shared product data library. Also, all three rely on the same data structure. So for this team, one repo removes the synchronization work.
When is a monorepo not a good idea?
It is not the right choice for every team, and we prefer to say that openly. In some cases, separate repos are healthier.
- Projects are truly independent and share no code.
- Different teams have very different release rhythms and approval processes.
- Security requires that some code stays open only to a narrow group.
- Nobody on the team can set up and maintain the build tools.
- You must keep an open source package apart from a closed product.
For access separation in particular, a repo boundary gives the clearest protection. Folder-level rules work. Still, a repo-level split is always the simpler and sturdier guarantee.
Team culture is another sign. For example, small, independent teams that move at their own speed may resent shared rules. In that case, however, the monorepo works technically but creates friction on the human side.
What do we recommend to a small team about a monorepo?
For small teams, a monorepo is often easier than you expect. The repo is small, so build time, size and permission problems have not appeared yet. Meanwhile, you feel the shared code and consistency benefits from day one.
Our field experience says this: if you have two or three connected projects, starting with one repo and splitting later is usually cheaper. However, merging repos later is typically more painful than splitting them. This is a starting suggestion, not a guarantee.
Many small teams who ask what is a monorepo really want to know one thing: is the extra complexity worth it? So our answer is yes if you share code, and not yet if you do not.
- Start simple: use the workspace feature of your package manager first.
- Decide on extra tools only when slowdowns actually begin.
- Define the folder structure early and clearly: apps in one place, shared packages in another.
- Assign ownership to shared packages, even in a small team.
If you want help deciding how to structure your software project, we can review the repository and architecture choices with you through our custom software development service.
How do you plan a move from polyrepo to monorepo?
Moving everything at once is risky. Go in small, reversible steps, so that if something goes wrong, your loss stays limited.
- Write the goal: State clearly what you want to solve, for example copied shared code.
- Choose a pilot: Start with the two projects that share the most code.
- Keep the history: If possible, merge the old repo histories so the trail of responsibility survives.
- Build the structure: Create the folders for apps and shared packages.
- Move the pipeline: Adapt your continuous integration flow to the new structure.
- Define ownership: Turn on code ownership and review rules together with the move.
- Measure: Compare build time and team satisfaction before and after the move.
Next, if the pilot succeeds, add the other projects step by step. If it fails, however, going back to the old setup should still be easy.
Do not turn the move into a mega project separate from daily work. Instead, split it into small work packages and place them in sprints. The approaches in our agile project management article help with that.
Communication matters during the move too. Tell the team in advance, explain the new folder structure on one page, and answer questions fast in the first weeks. Even a perfect technical move needs time for habits to change.
What should a practical monorepo checklist include?
Then, after you decide, the list below shows the points you are likely to miss. Tick the items one by one with your team.
- Is the folder structure written down, and does everyone read it the same way?
- Did you pick a tool that understands the dependency graph?
- Is the affected build and test flow connected to continuous integration?
- Does the build cache work reliably?
- Are the code ownership file and required review turned on?
- Did you evaluate the partial working tree option for a large repo?
- Did you define an independent release rule for each project?
- Do you scan for secrets such as keys and passwords inside the repo?
- Is a one-page onboarding guide ready for new developers?
If you answer no to more than half of the list, it is wiser to postpone the move and build the basics first.
How does a monorepo relate to AI-assisted coding and infrastructure code?
Let us touch two neighbor topics briefly, because we have separate articles for both. First, AI-assisted coding tools can work more consistently when their context is wide. Therefore, one repo can make that context easier, since it shows how an app relates to a shared library in one place. For details, see our article on what is vibe coding.
If you define infrastructure as code, whether to keep those definitions in the same repo as application code is a separate decision. A separate repo means separate permissions. Meanwhile, the same repo means one change can update the app and the infrastructure together. We covered this in our article on what is infrastructure as code.
If you build several front end apps, also read our piece on the difference between Next.js and React. Also, teams often choose a monorepo for these multi-app projects.
What are the most common monorepo misconceptions?
In practice, a few wrong ideas around the topic make the decision harder than it needs to be. So let us correct the most common ones.
- "A monorepo means everything ships together." No, projects can ship independently.
- "Only big companies need one." No, small teams are often the easiest starters.
- "Monorepos are always slow." No, with the right tools you run only the work you need.
- "Everyone can change any code in a monorepo." No, ownership rules limit that.
- "Once we pick it, there is no way back." No, moving in either direction is possible, it just takes effort.
In short, the common thread is mixing up the tool with the goal. The goal is a team that ships consistent software with less friction.
Conclusion: is a monorepo the right choice for you?
The short answer to what is a monorepo is this: many projects in one repository. The long answer is a balance. You gain shared code, atomic changes and consistency, but you must manage build time, permissions and repository size.
Then you can reduce the decision to three questions. Do your projects change together often? Do you copy shared code between repos? Does someone on your team own the tool setup? If you answer yes to all three, a monorepo is a strong candidate.
If you are unsure, then start with a small pilot and measure the result. As Talha Aslan and team, we recommend this path for software architecture decisions, because an easily reversible experiment is worth more than a long debate.



