π¦ How to correctly manage dependencies in a yarn workspace (#74)
There are two technically correct ways to manage dependencies in a yarn workspace. I want to illustrate why one way is clearly better than the other.
Letβs say that you have a monorepo thatβs structured in the following way:
βββ monorepo β βββ packages β β βββ package-a β β β βββ package.json β β βββ package-b β β β βββ package.json βββ yarn.lock βββ node_modules βββ package.json
Imagine that both package-a
and package-b
are using TypeScript.
The first option is to define all of your shared dependencies in the root package.json
file (monorepo/package.json
), and all other dependencies in each package's respective package.json
file. In this scenario, you would define TypeScript in monorepo/package.json
.
The second option is to define all of your dependencies in each package's respective package.json
file, even if those dependencies are shared. In this scenario, you would add TypeScript to both monorepo/packages/package-a/package.json
and monorepo/packages/package-b/package.json
.
This may sound counterintuitive, but option #2 is the better and more correct option in the long term.
Let's illustrate why option #2 is better by fast-forwarding this monorepo example by a couple of years. Let's imagine that your monorepo has expanded to include many more packages and apps:
βββ monorepo β βββ packages β β βββ package-a β β β βββ package.json β β βββ package-b β β β βββ package.json β βββ apps β β βββ app-a β β β βββ package.json β β βββ app-b β β β βββ package.json βββ yarn.lock βββ node_modules βββ package.json
This example is much more realistic for what a monorepo looks like at a relatively mature company.
In this scenario, all of the apps are written using React. package-a
and package-b
contain shared React components that are referenced by all three apps. All of the code in the monorepo is on React 17.
One day, the engineering organization decides it's time to upgrade to React 18. If you had React 17 defined in the root package.json
, you'd need to upgrade every single project all at once. There are a few reasons why this is undesirable:
-
You can't parallelize the work.
-
You can't test the upgrade path on lower-risk projects.
-
Your team has to review and QA a massive chunk of work, rather than multiple incremental chunks.
On the other hand, if each package and app manages its own dependencies you can upgrade React much more strategically.
-
Each team can slot in the upgrade where it makes sense in their roadmap.
-
Different teams can work on the upgrade simultaneously.
-
You can improve the quality of QA and reduce the potential for regressions by upgrading incrementally.
-
If you do introduce a regression, you can rollback only the small area of the codebase that introduced it, rather than having to revert the entire upgrade for the whole codebase.
Talk soon,
Mae