The Pros and Cons of Monorepos
Developing large-scale codebases with multiple teams, budgets, and deadlines comes with huge logistical problems. Typically, once an application grows to a certain size, developers attempt to split it into multiple parts to address separation of concerns. However, this multi-repo approach causes numerous kinds of issues that harm the development experience. Fortunately, monorepos offer a practical solution to this problem.
This article explores several concepts behind monorepos, including what they are, where they fit, and where they don’t. In addition, we offer some valuable tips from the perspective of a monorepo framework maintainer.
What Is a Monorepo?
A monorepo, as its name implies, is a Git repository that consists of multiple projects or packages. Typically, monorepo maintainers create a base project that manages the collection of those packages individually. Each package is isolated and contains its own dependencies, exports, and tooling.
Monorepos differ from monoliths in that they consist of a collection of compact, cohesive packages instead of one big package with many dependencies. In the early days of development, monoliths were the way to go because they were very simple to set up. However, monolithic repos had scaling issues and other inconveniences. Today, modern frameworks and applications overcome those issues by using monorepos – especially when many teams are working on the same project.
Naturally, it is possible to create many individual repos instead of a monorepo (this is called a multi-repo approach). This approach, however, causes great fragmentation between teams and a lack of cohesion within projects that need to share code. Therefore, unless you are working on a one-off project with minimal requirements, the optimal way forward is to go with monorepo development.
Next, let’s take a look at how it all started by exploring the history of monorepos.
History of Monorepo Development
Although most of the concepts and tooling behind monorepo development have surfaced in the past 5-6 years, this method of development is not new. Google pioneered the concept of monorepos quite early. Meanwhile, due to the complexities of managing large-scale projects under version control systems, many companies relied on internal tools to fragment projects into multiple packages.
The main idea behind monorepos stems from the concept of code sharing – particularly the systematic organization and the architectural strategy of developing small, independent, versioned projects that can be shared easily. With the emergence of specialized tooling like Lerna and Nx, monorepo development became more accessible.
When it comes to developing large-scale libraries and applications, monorepos overcome many problems. The availability of dedicated tools for monorepo development demonstrates the community’s strong interest in making those workflows easy to work with as well as performant.
Key Pros of Monorepos
There are many advantages of adopting monorepos for development, including:
- Easier discoverability of code: Developers can easily search the codebase for pieces of functionality that they can reuse or adopt, thus improving their development speed. For example, you can use GitLens or GitHub references to navigate from one reference to another without trouble.
- Easier dependency management: You can keep track of the dependencies of each project and enforce policies regarding minimum package versions. Tools like rush.js help you do that successfully.
- Shared CI/CD pipelines: You can share many CI/CD steps and introduce quality tools closer to the development experience.
- Shared Git history: When inspecting the Git history, developers can see the whole range of relevant commit changes instead of having to look at multiple commits across different repositories.
Overall, monorepos offer a great development experience that makes debugging and switching contexts easier than it is with the alternatives.
Cons of Monorepos
Still, there are a few disadvantages of monorepos that you should know about:
- Leaking abstractions: It’s easy to leak abstractions across packages. For example, a package might directly import a relative export from a different package without declaring its dependencies. This can be solved by automated tools like dependency-cruiser.
- Longer build times if not monitored: If things are left as they are, build times can take considerably longer. To mitigate this issue, organizations can build up dedicated internal tooling teams to shorten build times and improve automation.
- Global side effects: If there are parts of the project that do not work, it can affect the deliverability of your packages. If there is a change that breaks the build, for example, you won’t be able to release your package until the main branch is fixed. This issue also includes merge conflicts and security issues.
Learning to master the tricky parts of monorepos can help your team overcome some of these challenges and collaborate more effectively in the long term.
Monorepos in the Wild
Monorepo development is hot right now. Many open-source tools utilize this workflow because of its convenience as well as its ability to improve cohesion and code-sharing. Prominent monorepos include:
- Next.js: Next.js is a big monorepo project with many contributors. It uses yarn workspaces.
- Create-react-app: Create-react-app started as a monolith, but the maintainers turned it into a monorepo about 6 years ago using Lerna.
- Faust.js: Faust.js is a headless Wordpress framework. The maintainers have followed a monorepo development strategy using npm workspaces from the beginning. It has performed well so far, with only a few issues related to jest testing.
Those are only a few examples of the use of monorepos in modern tooling. We expect to see more adoption and quality of life improvements in the future. Speaking of improvements, here are some valuable best practices to follow when you are working with a monorepo.
Best Practices for Monorepo Development
Developing with monorepos encourages the adoption of best practices and conventional workflows. They are focused on supporting the maintenance of the bundled packages and the everyday development experience.
The following practices will help you support monorepos that will scale well over time:
- Use a dedicated monorepo build tool: Starting on your own without using a tool that is designed for the monorepo style is a dead end. You should set the project up for monorepo development before you write a single line of code. There are several good tools that address many of the nuances of monorepo development, such as yarn workspaces, Nx, and Turborepo.
- Automate the release channels: Next, you want to set up the release strategy and automation. You should create and automate your CI/CD pipeline to make sure it works, then publish your first version and make sure that works. Be sure to document your steps. This will make your life easier in the long run. If you are working with many teams, you should establish branch protections and minimum review counts to ensure that you don’t break up the main branches.
- Use changesets and semantic versioning: Be very consistent and cordial about the release versioning in the sense that you make sure not to deliver breaking changes without adequate notice and documentation.
- Look for optimization opportunities: Monorepos can get really large and chaotic if you don’t control what goes in them. Always look out for signs of problems (like slower builds, larger asset footprints, limited sharing of code, and so on) and find ways to address them with automation or better collective code reviews.
Monorepo development poses a few challenges that are solvable as long as teams are accountable to each other, follow good engineering practices, and look for future improvements. Monorepo can bring a great development experience and good scalability to an agile environment.
The current industry trend is to adopt monorepo development as the basis for all projects big or small, assuming that they will scale better over time. Many organizations start working with monolithic repositories and transition to a multi-repo approach only to find out that monorepos would have been better for some of their projects.
Taking the time and effort to weigh the options and decide what kind of development workflow you want to follow is extremely important. We hope that this tutorial serves as a great starting point.