Posted May 18, 2018 by Bryan Wain
Previously, we wrote about monorepos and how Yarn Workspaces makes working with them simpler. Unfortunately, moving to a monorepo is not always an easy choice. Without the right tooling, a monorepo can often harm the developer experience instead of help it.
The Problem With Monorepos
One of the main reasons projects usually move to a monorepo is because they make it easier to make changes across multiple packages by allowing you make those changes in a single pull request. One of the ways Yarn Workspaces improves this workflow is by automatically symlinking sibling package dependencies in the node_modules folder, allowing you to immediately see the results of a change to one package when working on another.
Unfortunately, while symlinking sibling packages all of the time makes cross-package development easier, it comes with a big downside when you want to work on a single package. If package
A depends on packages
C, you need to build all three packages just to make a change in package
A. In a monorepo with many packages, this can be a very slow process compared to a multirepo setup, where you would just pull down a pre-built version of
C when you install.
These opposing benefits of monorepo and multirepo development often leave developers with a difficult choice. Optimize for cross-package development with a monorepo or optimize for single-package development with many repos. We feel that this is a choice you should not have to make, and our goal with focused workspaces was to help turn Yarn Workspaces into a tool that gives you the full benefits of a monorepo without forcing you to give up the benefits of multiple repos.
Introducing Focused Workspaces
yarn install --focus is a new installation option available in 1.7.0 that shallowly installs a workspace’s sibling dependencies under its
node_modules folder. Shallow installation can best be explained with an example.
Imagine you have a monorepo with packages
A depends on
B depends on
External, which is not part of the monorepo. A normal installation might result in something like this:
| my_project/ | package.json | node_modules/ | A/ (symlink to packages/A) | B/ (symlink to packages/B) | External/ | packages/ | A/ | node_modules/ (empty) | B/ | node_modules/ (empty)
The problem here is that if you want to run
A, you need to build
B as well. If you haven’t made any changes to
B, this is most likely going to be slower than installing
B from the registry.
If you were to go to
packages/A and run
yarn install --focus instead, the result would look like this:
| my_project/ | package.json | node_modules/ | A/ (symlink to packages/A) | B/ (symlink to packages/B) | External/ | packages/ | A/ | node_modules/ | B/ (not a symlink. Pulled from registry. No need to build.) | B/ | node_modules/ (empty)
This allows you to run
A without rebuilding
B. Because of the way node module resolution works, when building
A, it will find
packages/A/node_modules/B and avoid using the symlink at the root, which points to an unbuilt version of
Yarn will always do a minimal install when using
--focus. Note that
External is not reinstalled under
A. There is no need because when
A tries to resolve
External, it will already point at the hoisted version.
Dealing With Version Conflicts
Let’s look at a slightly more complicated example. Imagine now that both
B depend on
A depends on
B depends on
B can no longer share a copy of
External because their versions are incompatible.
A regular install would produce this:
| my_project/ | package.json | node_modules/ | A/ (symlink to packages/A) | B/ (symlink to packages/B) | External/ (v2) | packages/ | A/ | node_modules/ | External (v1) | B/ | node_modules/ (empty)
and a focused install would produce this:
| my_project/ | package.json | node_modules/ | A/ (symlink to packages/A) | B/ (symlink to packages/B) | External/ (v2) | packages/ | A/ | node_modules/ | External (v1) | B/ | node_modules/ | External/ (v2) | B/ | node_modules/ (empty)
Yarn needs to not only install
A, but also install
External under the nested copy of
B to ensure that
B uses v2 of
A still uses v1.
If you want to remove the shallow installations from a focused install, just rerun
yarn install without
node_modules will return to the state they were in before you focused.
remove will also remove any shallow installations.
You can also focus on another workspace and the original workspace will unfocus.
Focusing by Default
If you would like to have a workspace always use
--focus when installing, you can use the CLI arguments feature of
If you add that to
packages/A/.yarnrc, you will always do a focused install when you run install from
A, but not from any other packages or the root.
If you instead add it to
packages/.yarnrc, you will do a focused install from all packages under
We hope focused workspaces helps make your migration to a monorepo easier. This is only a first iteration and further improvements will likely be made in the future. If you notice anything wrong or have any suggestions on how we can improve your experience with workspaces, please let us know.