How to NOT do monorepos

Who actually needs a monorepo? Monorepos exist to solve the problems that arise when you you have deeply coupled and interconnected projects. For example, Vue.js has many libraries that depend on it, like Vue-Router, Pinia, Vue-I18N, Vue-DevTools, Vue Test-Utils, etc. So when tweaking the internals of how Vue works, it's important to know that this new version of Vue won't break any of these other libraries, before doing a new Vue release. Another example is Jest, a unit-testing library. It solves dozens of unique problems around unit testing. And consequently those unique problems have been isolated into their own independent repos solely focused on solving their one problem in a version controlled way, like Snapshot testing for example. These other libraries all live in the same monorepo as Jest to make sure that as each is updated, they all work together happily! Yay! Why you don't want a monorepo Though they do solve the problems outlined above they come with some drawbacks. In the past monorepos were notorious for not being able to run on Windows machines. However, with npm Workspaces, this is no longer the case. But if you are using a different monorepo tool (Yarn, Lerna, Turbo, etc). You will have this problem. They just don't play nice on Windows computers. But you don't use Windows, and worse you're a selfish piece of shit that doesn't care about others, so this lack of support doesn't bother you (seriously, just use npm Workspaces, they're good). The real issue with monorepos is they have a ton of added complexity. It makes sense, you are combining all of the project setup complexity of all other projects into one system. So that requires it to be more complex than any of them would be on their own. Having multiple nested package.json files in a project also can cause issues with 3rd party tooling and editors. It's not always obvious where a dependency should be installed either. Initial setup of the repo is also much more cumbersome for new devs, as it requires a massive npm install to create a huge shared node_modules. Abstracting npm scripts across all your repos is more of a pain. Setting up publishing of all packages at the same time is a benefit but also complex, and results in much uglier and harder to use GitHub Releases pages. etc. etc. etc. It's a technology, it has pros and cons, big whoop. Some projects really do justify the need for this extra effort and hassle, but if you can avoid it, you should try your best to. You probably don't need a mono repo. Here's a case where you might think you need one.... but nehhhh you don't. Let's say you have a component library. You'll need to be able to build this library to a "dist" folder, and publish it to npm (including the package.json). You'll also need to create a documentation website. As you write a new component, it would be nice to document it as you go, so you can see the component live in the docs site locally and play with it as you build it to make sure it works. This means that you will be updating the docs site and the component library in the same branch at the same time. Pretty convenient. But the docs site needs it's own build process. So it can create a "site" folder of static assets that gets deployed. How can we solve this without all of the headache of a monorepo? How can we do this in a simpler way? Solution In this scenario the solution is actually pretty easy. Use multiple Vite config files. vite.config.lib.js This config file has all the build options for the /lib folder to create the /dist folder. It also has all the Vitest unit testing options for the component library set up. vite.config.docs.js This config file has all the build options for the /docs folder to create the /site folder. It also has options related to running the server locally for development. In your package.json you will have different npm Scripts: like: { "scripts": { "start": "vite --config vite.config.docs.js", "build:docs": "vite build --config vite.config.docs.js", "build:lib": "vite build --config vite.config.lib.js", "test": "vitest --config vite.config.lib.js" } } Then you can run npm start to start the docs site, or npm run build to build both the docs site and the library. Benefits/Drawbacks This system is dramatically simpler to deal with. It's just a few config files. However, the downside is you have one package.json for everything, and you'll need to put everything in the "devDependencies" section, unless you want users of your library to receive them when they npm install your library.

May 4, 2025 - 06:35
 0
How to NOT do monorepos

Who actually needs a monorepo?

Monorepos exist to solve the problems that arise when you you have deeply coupled and interconnected projects.

For example, Vue.js has many libraries that depend on it, like Vue-Router, Pinia, Vue-I18N, Vue-DevTools, Vue Test-Utils, etc. So when tweaking the internals of how Vue works, it's important to know that this new version of Vue won't break any of these other libraries, before doing a new Vue release.

Another example is Jest, a unit-testing library. It solves dozens of unique problems around unit testing. And consequently those unique problems have been isolated into their own independent repos solely focused on solving their one problem in a version controlled way, like Snapshot testing for example. These other libraries all live in the same monorepo as Jest to make sure that as each is updated, they all work together happily! Yay!

Why you don't want a monorepo

Though they do solve the problems outlined above they come with some drawbacks. In the past monorepos were notorious for not being able to run on Windows machines. However, with npm Workspaces, this is no longer the case. But if you are using a different monorepo tool (Yarn, Lerna, Turbo, etc). You will have this problem. They just don't play nice on Windows computers. But you don't use Windows, and worse you're a selfish piece of shit that doesn't care about others, so this lack of support doesn't bother you (seriously, just use npm Workspaces, they're good).

The real issue with monorepos is they have a ton of added complexity. It makes sense, you are combining all of the project setup complexity of all other projects into one system. So that requires it to be more complex than any of them would be on their own. Having multiple nested package.json files in a project also can cause issues with 3rd party tooling and editors. It's not always obvious where a dependency should be installed either.

Initial setup of the repo is also much more cumbersome for new devs, as it requires a massive npm install to create a huge shared node_modules.

Abstracting npm scripts across all your repos is more of a pain.

Setting up publishing of all packages at the same time is a benefit but also complex, and results in much uglier and harder to use GitHub Releases pages.

etc. etc. etc.

It's a technology, it has pros and cons, big whoop.

Some projects really do justify the need for this extra effort and hassle, but if you can avoid it, you should try your best to.

You probably don't need a mono repo.

Here's a case where you might think you need one.... but nehhhh you don't.

Let's say you have a component library. You'll need to be able to build this library to a "dist" folder, and publish it to npm (including the package.json).

You'll also need to create a documentation website. As you write a new component, it would be nice to document it as you go, so you can see the component live in the docs site locally and play with it as you build it to make sure it works.

This means that you will be updating the docs site and the component library in the same branch at the same time. Pretty convenient. But the docs site needs it's own build process. So it can create a "site" folder of static assets that gets deployed.

How can we solve this without all of the headache of a monorepo? How can we do this in a simpler way?

Solution

In this scenario the solution is actually pretty easy. Use multiple Vite config files.

  • vite.config.lib.js
    • This config file has all the build options for the /lib folder to create the /dist folder. It also has all the Vitest unit testing options for the component library set up.
  • vite.config.docs.js
    • This config file has all the build options for the /docs folder to create the /site folder. It also has options related to running the server locally for development.
  • In your package.json you will have different npm Scripts: like:
{
  "scripts": {
    "start": "vite --config vite.config.docs.js",
    "build:docs": "vite build --config vite.config.docs.js",
    "build:lib": "vite build --config vite.config.lib.js",
    "test": "vitest --config vite.config.lib.js"
  }
}
  • Then you can run npm start to start the docs site, or npm run build to build both the docs site and the library.

Benefits/Drawbacks

This system is dramatically simpler to deal with. It's just a few config files.

However, the downside is you have one package.json for everything, and you'll need to put everything in the "devDependencies" section, unless you want users of your library to receive them when they npm install your library.