My Experience with a Monorepo
We found ourselves having to share code between different services. Each shared library also had its own repository. Sometimes, making a change involved multiple repositories. Onboarding new people meant cloning a ton of repos. All the libraries needed to be installed.
There was a clear impact on productivity.
I decided to merge all the repositories into a single one. Common knowledge says this is a bad idea. More experienced engineers whose opinion I respect warned me against it. Still, I decided to give it a try.
After multiple years of working on the monorepo, I’m happy to say it has been great. Making changes to the system is easy. Onboarding requires cloning just one repo, and finding the code you’re looking for is easy. After making a change, you can run all the tests with a single command.
To make it work correctly, we did have to build some custom tooling. This tooling builds artifacts(docker images), run tests and applications.
Implementation
applications/
bin/
database/
docker-compose.yml
documentation/
libraries/
applications
- Each application lives in this directory. One key thing about applications is that they can’t import code from each other. If you let developers do so, they will, and you will have an ugly mess.bin/
- This directory has scripts for the monorepo’s tooling. Examples include building artifacts and ad-hoc commands ran in prod.database/
- Database migrations for the main database.docker-compose.yml
- Run the entire system locally.documentation/
- Processes for prod operations, how to deploy, and getting started as a new developer.libraries/
- Code shared between applications.
Everything needed for an application or library is on their directories. This means we can change our mind about having a monorepo, exporting the code is just taking everything inside the directory and placing it on its repository.
Tradeoffs
Cons
- We’re a small team. I know in the future this might not scale but, I’m OK with that. We can worry about merge conflicts when we are 100+ engineers.
- Could share too much code.
- Some initial tooling needs to be built. You will need to mess with
your programming language’s
PATH
to get imports to work in development/test.
Pros
- We can easily grep for code.
- Atomic commits.
- Easier upgrades/maintenance.
- Easier code sharing.
- Easier onboarding.