logo
Navigate back to the homepage

Monorepos, Yarn Workspaces and Lerna! What the heck?

Kamlesh Chandnani
April 20th, 2020 · 5 min read

What are monorepos?

  • Monorepos are normal git repositories but it gives you the ability to have multiple projects inside one repository so you can use these projects across each other just like any other npm package but still isolate the concerns by putting them in their own directory.
  • Just to emphasize IT’s NOT A MONOLITH.

How do you setup the development environment for something like this?

  • You might be wondering that since each package/project is an independent entity which means they have their own release cycle so how do you setup your development environment because while developing you would need to make changes across packages. Obviously you can’t publish each package for every single change then sit and keep bumping your versions in the package.json of the current project to get the latest changes.

  • Let’s imagine what I’m blabbering about. Imagine a repository with the following directory structure

1├── package-1
2│ ├── src
3│ │ ├── mock.js
4│ └── package.json
5├── package-2
6│ ├── src
7│ │ ├── date.js
8│ └── package.json
9├── package-3
10│ ├── src
11│ │ ├── api.js
12│ └── package.json
13├── main-app
14│ ├── src
15│ │ ├── app.js
16│ └── package.json
17├── dist (or build)
18├── node_modules
19├── README.md
20├── package.json
21├── .eslintrc
22├── .prettierrc
23├── babel.config.js
24└── .gitignore
  • The structure above describes how typically a monorepo looks like. This gives you the ability to use package-1, package-2, package-3 and main-app as dependencies within each other while developing locally and distribute them individually by publishing them to NPM.

  • All the packages can also share some configs like eslint, prettier, babel etc. defined at the root.

  • Each package has its own package.json which means they have their own development and release life cycle.

Isn’t it wonderful to see that everything is shared but also isolated at the same time? 🤩

The main question still remains unanswered i.e How do you make it work in real? One possible solution to make them work and what most of us were doing 5 years before was using npm link but we all know the pain of making it work and then explaining all our team members to set it up. People get frustrated and they literally just give up, then the task goes to a person who actually set it up 🤦‍♂

Don’t you think that all these things should be handled by a tool? It’s pretty obvious that we are in 2020 and we should have some tools to do this for us and make our life simpler.

NPM, Lerna and Yarn workspaces to the rescue!

These tools helps you setup monorepos, link packages locally and make your development environment buttery smooth.

But with a lot of tools comes a lot of confusion! When to use what? 🤷‍♂

Lerna, NPM and Yarn

Let me start with the brief description about each of them:

  • Lerna: It’s a tool for managing JavaScript projects with multiple packages. It comes with the collection of single-line commands that you can run across packages together instead of going into each package and running them individually. Here are some commands that Lerna provides lerna publish, lerna version, lerna bootstrap. You can read about the detailed documentation.
  • NPM and Yarn: They are our best friends and we all know about them I don’t need to describe them 😄

But what the heck is Yarn Workspaces then?

  • Yarn Workspaces helps you setup monorepos with multiple packages and manage them.

But wait wasn’t lerna supposed to do what yarn workspaces are doing? Why do we need Yarn workspaces then? 🤔

This is where the real confusion starts. Let’s dive deeper to understand each one of them.

  • As I mentioned earlier Lerna helps you manage monorepos with multiple packages with a collection of commands. One of the main tasks when you are working with monorepos is bootstrap i.e install all the dependencies of the packages inside your monorepo and link them internally with the help of symlink so you can make changes in one of these packages locally and use those changes in other packages without the need of publishing those changes to registry. Lerna provides a command for this i.e lerna bootstrap. It also provides you with other useful commands like lerna version similar to npm version for bumping your package versions, lerna publish similar to npm publish for publishing your packages and many more.
  • Yarn Workspaces handles just the bootstrapping part of monorepos i.e install all the dependencies of the packages inside your monorepo and link them internally. It doesn’t gives you any commands or utilities that Lerna gives you to manage your packages within monorepos. When you configure lerna with "npmClient": "yarn" yarn’s bootstarpping algorithm kicks in instead of lerna’s. Yarn’s bootstrapping algorithm has proven out to optimise resolution of the packages better over lerna and optimises .lock files with monorepos.

What about npm? Can’t we use monorepos with Lerna and npm?

We can of course use it but there are few things that I would like to highlight about why I prefer yarn over npm over here:

1. npm install fails when you run it from anywhere except the root of the monorepo.

1├── package-1
2│ ├── src
3│ │ ├── mock.js
4│ └── package.json
5├── package-2 // has package-1 as dependency
6│ ├── src
7│ │ ├── date.js
8│ └── package.json
9├── package-3 // has package-1 and package-2 as dependency
10│ ├── src
11│ │ ├── api.js
12│ └── package.json
13├── main-app
14│ ├── src
15│ │ ├── app.js
16│ └── package.json
17├── dist (or build)
18├── node_modules
19├── README.md
20├── package.json
21├── .eslintrc
22├── .prettierrc
23├── babel.config.js
24├── lerna.json
25└── .gitignore
  • Assume you have the above directory structure and now if you run npm install from monorepo root everything will work fine and all your packages would be linked locally. But if you run npm install from package-2 it’ll fail as it will go and find package-1 which is listed as a dependency but it won’t be able to find it on the npm registry as it’s not published. Ideally it should have linked it locally but npm is not aware about lerna. This issue also persists when you want to remove some dependencies from within some package for eg: remove some dependencies from package-2 using npm uninstall. There’s an open issue here.

2. .lock files doesn’t plays well when using npm as the npmClient in lerna i.e "npmClient": "yarn". It’ll generate package-lock.json per package and this makes it difficult to run any npm commands like install/uninstall from within the packages directory itself.

Benefits of using Yarn workspaces.

While using monorepos with yarn workspaces you get some benefits out of the box:

  1. Yarn mainatains just one yarn.lock at the monorepo root when workspaces are enabled.
  2. You can run yarn install/yarn remove either from monorepo root or from within any of the packages directory. Since it maintains just one yarn.lock file at the root running these commands also updates the yarn.lock.
  3. It helps to hoist node_modules by default at the monorepo root. Although this can screw up things sometimes(especially with react-native projects) but it’s fine for most of the case. You can also disable hoisting if you wish to.
  4. All the benefits of yarn also applies for example caching. This boosts the packages installing time drastically especially when your monorepo is growing and you don’t want to wait for a long time when you run yarn install.

FAQs

1. How do I setup yarn workspaces?

  • To enable yarn workspaces you just need to add private and workspaces keys to your package.json
1"private": true,
2 "workspaces": [
3 "package-1",
4 "package-2",
5 "package-3",
6 "main-app"
7 ],

2. How do I setup lerna with yarn workspaces?

  • add workspaces to your package.json as mentioned in point #1 above.
  • install lerna at the repository root.
1yarn workspace -W add lerna
  • create lerna.json at the repository root.
1{
2 "version": "1.0.28",
3 "npmClient": "yarn",
4 "useWorkspaces": true
5}

3. Do I always need lerna when working with yarn workspaces?

  • No. Lerna just provides you with set of commands that helps you manage the release, build, publish tasks of multiple projects with simple single-line commands. So you can use yarn workspaces independently if you don’t want any of the above mentioned tasks or if you already have your own scripts to do that.

4. Is there any alternative to lerna?

I’ve created this this repository to demonstrate the setup of monorepos with lerna and yarn workspaces.


If you have ideas or thoughts around monorepos or want to share your experiences with monorepos(Good/Bad) then you can write it to me or you can DM me on Twitter. I would love to hear them!

P.S. If you like this, make sure to subscribe, share this with your friends and follow me on twitter 😀

Join the Newsletter

Subscribe to get the latest write-ups about my learnings from JavaScript, React, Design Systems and Life

No spam, pinky promise!

More articles from Kamlesh Chandnani

Thoughts about Work Culture

Work culture plays a significant role in building a great organisation.

April 13th, 2020 · 6 min read

Let's refactor our Interview Process

There's a massive disconnect between the interview process and the work we do at work on day to day basis.

March 30th, 2020 · 10 min read
Link to https://github.com/kamleshchandnaniLink to https://twitter.com/@_kamlesh_Link to https://www.youtube.com/playlist?list=PLpATFO7gaFGgwZRziAoScNoAUyyR_irFMLink to https://linkedin.com/in/kamleshchandnani