Contents
A monorepo is a single repository containing several applications and libraries: backend, frontend, mobile app, shared packages. It makes shared code, coordinated changes across parts and common rules easier, but needs tooling that builds and tests only what changed. A monorepo suits one team or several closely linked teams on a shared stack.
Here is how a monorepo differs from many repositories, which problems it solves, which tools it needs and when it does not fit.
How does a monorepo differ from separate repositories?
With separate repositories (polyrepo), each application and library has its own repository, CI and versions. In a monorepo everything lives together and the relationships between parts are visible in the code.
| Criterion | Monorepo | Separate repositories |
|---|---|---|
| Shared code | Imported directly | Published as versioned packages |
| Changing an API and its client | One pull request | Several coordinated releases |
| Common rules | One linter, formatter, CI | Drift over time |
| Access control | Harder to separate | Simple — per repository |
| CI | Must build only what changed | Each repository separately |
| Team independence | Lower | Higher |
Which problems does a monorepo solve?
A monorepo solves consistency problems: when changes in one part almost always require changes in another, and shared code keeps getting duplicated or falling behind on versions.
- atomic changes — an API field and its use on the frontend change in one pull request;
- shared types — API types and schemas are used by server and client without manual copying;
- shared packages — the design system, utilities and configs live in one place;
- common tooling — the same linters, formatting and dependency versions;
- easy onboarding — a new developer clones one repository and runs everything; see developer onboarding.
Photo: Unsplash
Which tools does a monorepo need?
Without tools a monorepo quickly turns into slow CI where every change builds and tests everything. You need a workspace manager, a tool that understands dependencies between packages and caching of build results.
{
"name": "acme",
"private": true,
"workspaces": ["apps/*", "packages/*"],
"scripts": {
"build": "turbo run build",
"test": "turbo run test --filter=...[origin/main]"
}
}
What must be set up:
- Workspaces — pnpm, npm or yarn workspaces link packages together.
- Dependency graph — Turborepo or Nx know which packages depend on what changed.
- Building only what changed — CI tests affected packages, not everything.
- Caching — build and test results are reused across runs and developers.
- Boundary rules — a linter forbids importing other modules' internals.
On setting up pipelines, see CI/CD with GitHub Actions.
When does a monorepo not fit?
A monorepo does not fit when parts of the system belong to independent teams or companies with different release cycles, access rights and security requirements. For example, if a contractor should only access the mobile app, a separate repository is simpler.
- code from different customers or contractors who must not see each other's work;
- parts on different technologies with no shared code;
- a team not ready to maintain build and caching tooling;
- projects that almost never change together.
On boundaries between parts of a system, see monolith vs microservices and micro-frontends.
How do you structure a monorepo?
A convenient structure splits code into deployable applications and packages used by those applications. Dependencies must flow one way only: applications depend on packages, not the other way round.
apps/— the web app, admin panel, mobile app, backend;packages/— design system, API client, shared types, linter configs;- every package has an owner and a clear public interface;
- shared versions of key dependencies across the repository.
In short
- A monorepo keeps several applications and libraries in one repository.
- It simplifies shared code, atomic changes and common rules.
- Without building only what changed and CI caching, it becomes slow.
- It does not suit independent teams with different access rights and releases.
- Split code into apps and packages, with dependencies flowing one way.
FAQ
Can a PHP backend and a JavaScript frontend share one repository?
Yes, but the benefit is smaller than for single-language projects: no code is shared between them. The main gains are a shared API contract, atomic changes and a single CI.
Won't the repository get too big?
For most companies, no. Size problems start at very large scale, and partial clones and sparse checkouts exist for them.
How do you move to a monorepo?
Move repositories one at a time while keeping commit history, set up a shared build tool and CI, then gradually extract shared code into packages. Do it in a quiet period without big releases.



