Recently, I spent some time evaluating a quite popular monorepo tool – Nx. I was trying to understand what it can offer vs how much you need to invest in this tool’s maintenance.

Here’s what I found (these are my personal opinions and your mileage might vary).

What does Nx offer…

…according to their documentation:

  • running tasks (including distributed execution)
  • caching task results (including remote caching)
  • visualising the project graph
  • automation of updating dependencies
  • enforcing module boundaries
  • editor integration

What does Nx actually offer…

…when you have an existing monorepo with a Docker + CI/CD setup and you’re mostly interested in the local experience:

  1. Running tasks hierarchically
  2. Caching task results locally
  3. Visualising the project graph
  4. Adding tools with ease
  5. Generating new projects with ease
  6. Managing dependencies' versions

…but when you also have a package.json files for each project, with battle-tested TS + ESLint + Jest configurations, the list gets a little smaller

  1. Running tasks hierarchically
  2. Caching task results locally
  3. Visualising the project graph
  4. Adding tools with ease
  5. Generating new projects with ease
  6. Managing dependencies' versions

honest-work

It is beacause you have to pick one of two Nx flavours when adding it to your monorepo, and I believe that one choice is better than the other.

Two Nx flavours

It’s support and carry. The package-based approach and the integrated approach.

The package-based approach

The package-based approach is where your projects inside a monorepo contain a package.json (manifest) file. Each manifest is responsible for managing the dependencies of a package. The imports from other internal packages are (usually) made as if they were regular npm-hosted 3rd-party packages.

This is supported by literally all of the monorepo tools other than Nx. Nx even mentions it on their website, listing Yarn as one of the supporting tools.

What does it mean to me? If I ever decided to drop Nx, I don’t have to modify my repository (too much) to make it working with other tools.

The integrated approach

This is the batteries-included way of using Nx.

The most important thing to know about it, is that there is a single manifest file in the whole repository. All of the dependencies and devDependencies for all packages are defined in that single package.json.

The packages are referencing other packages using TypeScript path aliases, which are also stored in a single tsconfig.json file in the repository’s base directory.

This approach is the approach Nx wants you to use. All of its features related to pre-configured tools (compiler, linter, test runner) are working seamlessly on the assumption that you run them inside an integrated monorepo. They usually try to add or modify the configuration files in the base directory