Choosing monorepo tooling: nx.dev vs Turborepo for a green field projects in 2022

Analyzing what is a right tool for the job before committing to years of regrets

Sergey Dubovyk
4 min readAug 4, 2022
Git is so simple, yet it can be so messy if done wrong

In a galaxy far, far away

Nobody likes to skim through countless repositories and updating a single dependency. Copying typings, helper functions, and similar “stuff” is even more boring.

Now we are starting a new project at Anyline GmbH. Previously we had a lot of relatively small repositories. Some of them were for the micro-services which are still alive and well, others remained stagnant for over a year. After a tedious refactoring by my colleagues, we were able to reduce this number to just under 100 repositories. But the problem remained: we had a lot of code duplication between packages, complicated deployment of breaking changes, and fairly many places to look at for pieces of documentation applicable for several entities.

The decision we came up with was to move to a monorepo for a new large project and possibly migrate the existing code base to it over time. Today there are quite a lot of existing solutions for managing monorepos for all stacks imaginable, but we focused on:

  • Pure yarn workspaces, for their minimalism
  • Nx.dev, as one of the most mature projects in the JS/TS ecosystem
  • Turborepo, as a relatively new (released late 2021) but promising solution backed by Vercel

As for the requirements, we came up with this list:

  • 📚 Top-level dependency management for JavaScript / TypeScript
  • 🔍 Uniform linting configuration
  • 🏗 Incremental builds
  • 📦 Caching of the build steps, ideally between development environments
  • ⏳ Working out-of-the-box hot module reload for Next.js application for imported packages
  • 😎 Overall “developer experience”

Unfortunately, with the default workspaces, we would need to implement most of the items above ourselves, so the selection narrowed down to nx.dev vs turborepo.

So, here it goes…

Dependency management

A lot of us had to deal with cases when you have 10 services, 5 of which are using a recent-ish version of some important package and the other 5 are a mix of everything between several minor patches old and a few years and three major releases outdated. This gets tedious fast. Thus we were looking for a tool that would take care of centralized dependency management for all the packages.

What does nx.dev has to say

In the case of nx, it is a part of their design philosophy to pay the upfront cost of synchronizing the dependencies’ versions for the benefit of a more maintainable and reliable codebase.

Unfortunately, it is not the case with the Turborepo. By default, it organizes dependencies on the “per-package” level. This, in my opinion, is too granular for the codebase which we aim to keep cohesive.

Nx 1— Turborepo 0

Uniform linting

Nx allows configuring default linting solutions when initializing a monorepo and then customizing it for each package. Also, it uses the tags system to provide a more granular way to control linting rules when working with more complex dependency trees.

Turborepo also by default creates a package with an ESLint config from which other packages can inherit.

Nx 2 — Turborepo 1

Caching & Incremental Builds

Nx uses two main types of caching:

  • Local, which is a fairly self-explanatory concept: you run a build, artifacts are cached as a whole and partial build artifacts, and on the next build you have a chance to reuse your work.
  • Distributed, which builds upon the previous model, but also synchronizes the caches between compatible development environments which makes even the first “local” build as fast as the ones with existing caches.

Unfortunately, distributed caching is a SaaS option provided by nx.dev.

As for the turborepo, it also supports local and remote caching. And again, as is the case with nx, remote caching is available as a part of the SaaS solution, this time powered by Vercel.

Overall both products are on par with each other regarding caching.

As for the incremental builds, both tools can detect the updated dependencies and optimize the build path. No clear winner here for me.

Nx 3 — Turborepo 2

Hot reload

Both nx and turborepo worked fine with Next.js hot module reload for me. Though there might be some workarounds that one will need to implement with turborepo, the overall experience is fairly good with both tools.

Developer experience

This is mostly a subjective topic, so everything here one should take with a grain of salt. Nx comes with a fairly mature CLI tooling as well as a VS Code and JetBrains plugins. On Turborepo’s side, there are no IDE plugins as of summer 2022 and it seems to be the official developers’ position for the time being.

Also, the nx provides a much more developed ecosystem due to it being on the market for almost 5 years now. Probably, Turborepo will get to this level eventually, but now it is still in a fairly early stage of its life.

Finally, my experience was that nx builds take less time both due to incremental builds, and distributed (and local) caching. Also, I felt it was more straightforward and “native” to add packages in languages other than JavaScript or TypeScript as well.

Nx 4 — Turborepo 2

Summary

I feel that nx provides better performance and developer experience. Turborepo is just a bit too young and its ecosystem is only beginning to mature. Probably, in a couple of years, it will be a strong competitor to other solutions, as the community already shows the new tool great love. But for now, I believe it’s better to stick to what already works and is “battle-proof”.

--

--

Sergey Dubovyk

Software engineer (TypeScript, Python, Golang, Rust)@ Anyline lurking around searching for amazing stuff