February 27, 2022
A monorepo is a consolidated repository containing the source code of multiple projects, which are commonly managed by independent teams and also often share common packages.
Recently a common software development practice is to have a full stack JavaScript application, this leads to many developer teams believing they have a Monorepo “Because all of my code is in the same repository”.
A monorepo is far more than just code collocation, we’ll address the benefits, drawbacks and showcase two industry leading Smart Build Systems.
There are a number of important benefits to using a Monorepo (with a Smart Build System).
There aren’t many downsides to using a Monorepo, but there are some that are worth mentioning:
base is easier than ever, the easiest solution with no additional dependencies are NPM Workspaces, and the other two solutions we will cover are NX and TurboRepo. ## Workspaces - Keep It Simple Stupid Workspace is a generic term that refers to a set of features in the NPM cli which adds support for managing multiple packages from the root of the repository, NPM workspaces allow you to deduplicate node modules and run commands against multiple projects at the same time. Below is an example structure of a repository using NPM workspaces
├── package.json
└── packages
├── package-a
│ └── package.json
└── package-b
└── package.json
# ./package.json
{
...
"workspaces": ["./packages/*"]
}
Workspaces provides two basic features, as for the most part Workspaces are just code collocation with the ability to run projects from one terminal in conjunction.
npm run test --workspaces
Ideal for đź’ˇ
NX is one of the most advanced build systems available and offers some incredibly powerful features to take your codebase to the next level.
Some of the most acclaimed features of NX are as follows:
Create a new NX project, with a Next.js application
npx create-nx-workspace@latest
The NX CLI will then ask you which template you would like to use to create this new application, for our case we will choose “Next.js”.
One common usage of NX is to create a shared library which can be used by multiple apps. Shared libraries are created in the libs
directory by default.
nx g @nrwl/react:lib ui-shared
Running this command generates a new ui-shared
library, which can be used by multiple apps. The beauty of this pattern is that if you change ui-shared
, it will rebuild other apps that use it, whereas, if you make a change in next-app
NX won’t rebuild ui-shared
.
Here you can see the process of importing our new shared library, and using it in our Next.js app.
When you run nx test next-app
, you are telling Nx to run the next-app:test
task plus all the tasks it depends on.
For a small project this is okay, however for a large project you really only want to run the tests for the files and changes you’ve made to the app.
To handle this, NX has the affected
command. nx affected --target=next-app
will figure out which tasks to run, and only run those tasks.
To visualize these “affected libraries”, use the nx affected:graph
command, in this example below we’ve added a second-shared
lib which is also used in next-app
.
Now we’re going to make a change to second-shared
, as this is used by next-app
, and next-app-e2e
you should expect that they should both be re-built.
As epected you can see that the next-app
and next-app-e2e
projects are affected, but the ui-shared
lib is not.
Once you’ve got an established NX Monorepo, you will likely have many applications and libraries. It is common that you will only be editing a small subset of these applications at once, Incremental builds allows NX to only rebuild a small subset of the libraries, which results in much better performance.
This then works closely with NX Cloud. With NX Cloud you and your team mates share the same cache, allowing you all to benefit from improved performance.
The above chart is from NX and highlights the performance differences between normal builds, and incremental builds (both cold and warm).
Running the Nx incremental build having already cached results from a previous run or from some other coworker that executed the build before. In a real world scenario, we expect always some kind of cached results either of the entire workspace or part of it. This is where the teams really get the value and speed improvements.
Ideal for đź’ˇ
nx.json
files and tuning them to your likingTurborepo is a recently open sourced project acquired by Vercel, the company which brought us Next.js.
Turborepo’s goal is to take what’s great about other build systems such as Lerna, and NX, whilst shipping it in a small simple package, which works hard to stay out of your way.
Setting up Turborepo is as easy as it gets, just run npx create-turbo@latest
.
This sets up an example project, with a web
and docs
apps, and a shared ui
package. Unlike NX there is a lot less boilerplate and this resembles a typical project structure you’d see with a “normal” Monorepo, this makes Turborepo much more approachable to a monorepo newcomer.
Turborepo has its own flavour of affected
from NX, both work very similarly, however Turbo repo doesn’t produce an interactive graph like NX does, Turborepo has a basic graphviz image export.
Turborepo has a few other features:
Ideal for đź’ˇ
Below is a feature comparison for Workspaces, NX, and Turborepo.
Feature | Workspaces | NX | Turborepo |
---|---|---|---|
Node Module Deduplication | âś… | âś… | âś… |
Run Commands Across Projects | âś… | âś… | âś… |
Shared Libraries | âś… | âś… | âś… |
Profile in Browser | ❌ | ❌ | ✅ |
Dependency Graph | ❌ | ✅ | 🚧 (non interactive) |
Incremental Builds | ❌ | ✅ | ✅ |
Cloud Caching | ❌ | ✅ | ✅ |
Code Generation | ❌ | ✅ | ❌ |
Distributed Task Execution | ❌ | ✅ | ❌ |
Hopefully you now appreciate the importance of a Smart Build System within a monorepo. As much as I’d recommend giving both NX and Turborepo a shot, if you don’t have time to experiment with both, I recommend trying NX.
Turborepo has most of the features that NX has, but NX does that and more, in a more performant package. Given the current state of turborepo due to it’s recent inception, I’d rather have a NX monorepo that has been battle hardened which you know won’t let you down.
However, with Turborepo’s recent backing of Vercel, I expect NX to face some strong competition in 2022, and as you all know, competition brings innovation.