Monorepo via Rush and pnpm
Decision
We use Rush and pnpm to manage all private code in one monorepo.
Problems
We have multiple packages with different dependencies that we work on and need to deploy independently. At the same time, some packages are dependent on each other, and it can be hard to organize changes that affect multiple packages at once.
Context
Currently, we only use JavaScript as development language. Most of our code is in a monorepo managed by Rush and pnpm, but the current setup sometimes couples packages in a way so that even small changes cause long test and build times.
We had some trouble with the monorepo, mainly on managing package versions. Not everyone was happy with the CI setup either, and the monorepo enforced the same setup on all developers.
See also https://monorepo.tools/ for more information.
Options
- Multirepos
- Monorepo with pnpm
- Monorepo with Rush
- Monorepo with Turborepo
- Monorepo with Nx
- Monorepo with Lerna
Reasoning
Why a monorepo instead of multirepos
Having a monorepo has multiple advantages over multirepos:
Mainly, it makes it easier to link and document between code in different packages, which makes it easier to keep an overview of how code belongs together and to keep it consistent. This is especially helpful when working full-stack. The same holds true for end-to-end testing.
Monorepos also allow us to bundle changes that affect multiple packages and need to be released together. This might incur downtime if done for applications that are deployed independently, but for package dependencies, it can be helpful to get a package working in an application without having to deploy fix versions for each small change.
On the other hand, multirepos are a bit better supported from tooling. E.g. GitHub doesn't currently support putting workflow files into sub packages.
Another tradeoff is that monorepos make it easier to enforce similar tooling across multiple packages, which can be good or bad depending on how it is used.
Why pnpm and rush as monorepo tools
We already use both pnpm and rush, which makes it easier to use. pnpm
allows us to identify dependencies: Which packages actually changed since the
last commit?
rush then identifies how to build dependencies before dependents.
Our developers are already used to this combination. They are built to work with each other, and since Rush is supported by Microsoft, chances are, that it will be supported for some while. There are advanced features like build caching, and both have been around for a while and are stable enough.
Turborepo is easier to configure, but quite new and therefore not as battle-tested. Also, some of the most interesting features are free in alpha, and most likely will require to sign up a paid Vercel account to use in the future.
Nx offers more additional features like scaffolding projects. But it has a big
drawback: Only one central package.json is used to manage all dependencies.
lerna is mostly focused on managing multiple publishable packages, not
applications.
Consequences
We will have to move existing multirepos into our monorepo. To ensure proper setup, we will also need to move the existing monorepo in small steps.
We will also need to make significant changes compared to the existing setup to be able to have simpler workflows in the new monorepo:
- Disable version policies for repositories that are not aligned yet
- Disable changelog functionality for packages that don't use it
- Ensure that packages that do not depend on each other are built independently
We will have to train new developers and some of the existing ones on how to use rush and pnpm.
We might need additional effort to include tools which do not have native monorepo support.