Stacks
Monorepo or separate repositories: what fits your team
If one team builds one product, choose a monorepo. Separate repositories fit parts that have their own team, their own release rhythm or their own access rules.

If one team builds one product, choose a monorepo: frontend, backend, shared code and infrastructure in a single repository. Separate repositories fit better when parts are independent of each other, with their own team, their own release rhythm or their own access rules. For a distributed team the deciding question is how easily someone can find the code, review it and run it locally.
What a monorepo gives you
A change that touches both the API and the frontend lives in one pull request. The reviewer sees both sides and they ship together. With separate repositories you have two pull requests that you need to merge and deploy in the right order.
Shared code is just a folder. Types, validation, a design system and the linting and TypeScript configuration sit in one place. You do not have to publish internal packages or track their versions.
A new developer clones one repository, reads one README and starts everything with one command. That makes a big difference in the first week (see also onboarding a remote developer).
What a monorepo costs
If CI builds and tests everything on every change, it gets slower as the repository grows. Nx uses nx affected to work out which projects a change touches and only runs tasks there. Turborepo stores task results in a cache, which you can share with your team and with CI through a remote cache.
Ownership blurs. When everyone can touch everything, nobody feels responsible for the payment module. A CODEOWNERS file on GitHub records who owns which folder. Those people get review requests automatically, and a branch protection rule can make their approval mandatory.
Read access is all or nothing. On GitHub you set read permissions per repository. CODEOWNERS decides who has to review, not who can see the code.
Upgrades touch everything as well. One version of React or TypeScript across the repository keeps things tidy, but a major upgrade then has to land everywhere at once.
When separate repositories are better
- Parts have their own team and release rhythm, such as a mobile app next to a web platform.
- An external party may work on one part and must not see the rest.
- A component is open source or used as a package by others.
- The parts share no code, and you want to be able to hand them over or sell them separately.
The price is coordination. A change across repositories needs versions, changelogs and a clear contract between the parts. An OpenAPI specification helps (see designing API-first).
What it means for a distributed team
| Monorepo | Separate repositories | |
|---|---|---|
| Onboarding | One repository, one README | Work out each repository separately |
| Change across API and frontend | One pull request | Several pull requests in the right order |
| Code review | Reviewer sees the whole picture | Reviewer sees one part |
| CI speed | Needs caching and building only what changed | Small and fast per repository |
| Restricting read access | Not possible per folder | Set per repository |
When you do not share an office, you cannot walk over and ask how something works, so it helps when the repository explains itself. Describe in the README how to run everything locally, add a pull request template and keep CODEOWNERS up to date. I wrote a separate piece on reviews in a distributed team: code review in a remote team.
How I would start
- One repository with an
apps/folder for what you deploy and apackages/folder for shared code. - Your package manager's workspaces to link those folders together.
- A
CODEOWNERSfile from the first week, even while the team is small. - Nx or Turborepo once CI gets noticeably slower, and not before.
- Split off a part only when there is a concrete reason: access, its own release rhythm or a different team.
Start with less tooling than you think you need. A monorepo with two apps and one shared package does fine with workspaces and a clean CI configuration.
Changing direction later
Pulling a part out of a monorepo is quite doable. Git tooling lets you move a folder, with its history, into a new repository. Merging separate repositories takes more work, because you have to align build scripts, CI and dependencies. So start with one repository and split when you need to.
Whichever you choose, write it down in a short decision record inside the repository. A new developer then sees why things are set up this way, and a year from now you still know why you made the call.
Sources
Frequently asked questions
Is a monorepo a good idea for a small team?
Yes, especially for a small team working on one product. You get one place for the code, one CI setup, and a change across frontend and backend fits in one pull request.
What tooling do you need for a monorepo?
To start with, only your package manager's workspaces and a CODEOWNERS file. When CI gets slow, add a tool such as Nx or Turborepo that only builds what changed and caches task results.
Can you restrict access per folder in a monorepo?
Not for reading. On GitHub read access is set per repository, and CODEOWNERS only decides who has to review. Code that not everyone may see belongs in its own repository.
About the author

Erik Schoorl
CTO and lead architect
Erik founded ESBL together with Bas. He is CTO of Brainpower Group and has final responsibility for architecture and delivery.
More from Erik





