vx exists because of a gap, and the gap is easiest to describe by what
sits on either side of it.
Turborepo: fast, and it stops
Turborepo got the important thing right. One turbo.json, a
content-addressed cache, dependsOn micro-syntax that reads the way
you think, a --filter DSL borrowed from pnpm. It is quick on a warm
cache and it stays out of the way.
It also stops. It runs a task here and caches it, in Vercel's remote
cache or in a self-hosted one through its open cache API; past that,
there is no remote execution and no seam to add one. There is no way to
change where a task runs. The config is JSON, so a shared input list is
a globalDependencies array you keep in sync by hand, and nothing
computed can participate in a key. Inputs default to every file in the
package, which turns a README edit into a rebuild and adds hashing to
every task. The flag surface is the largest of any tool in this space.
Turborepo is the right tool until the repository is large enough or
the team needs something it cannot do, and then there is no next step
inside it.
Nx: scales, and it is a product
Nx got the other important thing right. It has a project graph that
scales, affected that works, and a plugin model that reaches into
Rust, .NET, Java and Gradle. The company behind it is serious about
large repositories.
It is also a product, and the runner is the part of the product that
gets you to the rest. Several features that make a large monorepo bearable, distributed task
execution, flaky-task detection and the analytics, are Nx Cloud, a paid
service. The graph view is free, and a remote cache can be self-hosted.
The
open-source runner carries a daemon that is on by default, a heavy
schema (project.json, nx.json, namedInputs, targetDefaults,
executors wrapping every tool behind a JSON options object), and a
cached-run cost that is not in the same league: on the same 3,270-task
workspace, a fully cached run takes 6.45s against Turborepo's 463ms and
vx's 393ms (vx 16× and 18% faster), Nx's daemon off as in CI. Its cold
build burns 52 seconds of CPU where Turborepo burns 21 and vx 17 (vx
3× and 22% faster).
Benchmark workload: a synthetic monorepo of 1,090 packages and 3,270 tasks in 100 dependency layers, every build and test taking 1 s; real repos with uneven task times will differ.
Nx is the right tool if you want the platform. If you want the runner,
you pay for the platform's weight and are steered toward its price.
The gap
Between them is the thing a large JavaScript monorepo actually needs:
- a runner as small and as fast as Turborepo's, on the warm path and the cold one;
- a key that is correct in the cases Turbo's is not: computed config, strict outputs, no spurious miss at a commit;
- seams for the things Nx sells, remote execution, remote cache, task scheduling, telemetry, so that they can be built by anyone, on any wire, without the runner having an opinion about who provides them;
- no daemon, no account, no cloud, no dashboard, nothing that needs a business model to keep working.
Nobody was building that, for a reason that is not technical: the
seams are where the money is. A runner whose remote execution is a
plugin on a public API is a runner nobody can sell a cloud for. vx is
built that way on purpose and ships nothing distributed in its own
repository. @vzn/vx-reapi, which does remote cache and remote
execution against any Bazel Remote Execution API server, exists to
prove the seams are wide enough for someone else to build the
platform, and to make sure no one has to.
The bar
vx has to clear the same bar as both of them, and the way to know is
to run their tests. The parity suite in the repository takes the
behaviours a Turborepo or Nx user would reach for, spells each one in
vx, and pins it with a test that runs the real CLI. Where vx diverges
on purpose (bare task names never widen an anchored pkg#task's
scope; there is no --parallel because dependsOn is explicit) the
divergence is documented as such. The matrix is
Compared to Turborepo, Nx, vite-task, and every
claim in it cites a file in the upstream repository so it can be
re-verified as they change.
That is what "no choice on the market" meant: not that the others are
bad, but that they are each half of the tool, and the halves do not
combine. vx is the whole runner and nothing else.
Originally published on the vx blog. vx is an MIT task runner and build cache for JS monorepos: github.com/vznjs/vx.
Written with AI assistance.
Top comments (0)