DEV Community

TECHDEV
TECHDEV

Posted on Originally published at github.com

Node 26 is LTS today. Should you upgrade? Measure your own app instead of trusting a headline benchmark.

Node 26 entered Active LTS today, 2026-10-28. If you run Node 20 or older,
your runtime gets no more security fixes. If you run 22 or 24, you need a
plan for the move. And if you are choosing between Node and Bun, what you
have today is headline benchmarks: one author's app, on one machine, that
is not yours.

Nobody's tool answers, for your app on your machine: does it load on the
target runtime, how fast is it there, and should you upgrade — with
reasons, not a headline. So I built one: runverdict.

What it does

runverdict is a local, zero-dependency TypeScript CLI. No account, no
network beyond localhost, no downloads, no data leaves the machine. It
never downloads a runtime: it measures only runtimes already installed
and reports the rest as not-installed.

For each runtime it reports:

  • Version and EOL status from a built-in schedule table (Node only).
  • A load check: the entry is spawned, and a failure records the stderr tail verbatim — which is what names a broken native addon or a missing module.
  • Startup timing: median of 5 spawns, spawn to first stdout line.
  • An HTTP workload with the built-in http client only: requests/second, p50/p95 latency, and peak RSS, using the same workload, in the same order, on the same machine for every runtime.

Then it returns a deterministic verdict:

Verdict Rule
BLOCKED The target runtime fails the load check, or no runtime produced a measurement. Reasons name the failing runtime and its stderr tail. Exit code 1.
UPGRADE-READY The current runtime is EOL or Maintenance, the target runtime loads, and its req/s is not worse than the current runtime's by more than 10%. Exit code 0.
STAY Otherwise, with reasons and numbers. A measured regression beyond 10% is a reason to stay, stated with both numbers. Exit code 0.

The current runtime is the first runtime in --runtimes order with a
measurement; the target is the best-performing other measured runtime,
if any. With a single measured runtime there is no target to compare
against, so the verdict is STAY.

Built-in Node EOL schedule

Node major Status EOL
20 EOL 2026-04-30
22 Maintenance 2027-04-30
24 Maintenance 2028-04-30
26 Active LTS 2029-04-30

An unknown Node major is reported as UNKNOWN, never guessed. Bun has
no LTS/EOL schedule: runverdict reports the Bun version only and never
invents an EOL date for Bun. Deno is explicitly out of scope in v1: it
is not detected, and passing --runtimes deno is an error, not a silent
omission.

How to run it

Requires Node >= 20.

git clone https://github.com/techdevfun/runverdict
cd runverdict
npm install
npm link
Enter fullscreen mode Exit fullscreen mode

The repository ships a zero-dependency sample app with /health and
/json endpoints (examples/sample-app.js):

runverdict runtimes
runverdict measure --entry examples/sample-app.js --url /json --runtimes node,bun --duration 5 --connections 20 --json out.json
runverdict --version
Enter fullscreen mode Exit fullscreen mode

--duration defaults to 10 seconds and --connections defaults to 20.
The JSON manifest carries runtime versions, EOL statuses, every measured
number, run counts, the verdict with reasons, and the machine note:
"single-machine measurement, not a general claim".

Exit codes: 0 when the verdict is UPGRADE-READY or STAY; 1 when the
verdict is BLOCKED or when no usable runtime measurement exists; 2 for
a usage error.

Demo

A real demo run on the build machine (node v24.22.1, bun 1.4.4) measured
the sample app: node startup median 114 ms, 12,675.8 req/s, p50 1.0 ms,
p95 4.0 ms, 81.3 MB peak RSS; bun startup median 33 ms, 12,614.8 req/s,
p50 1.0 ms, p95 4.0 ms, 60.8 MB peak RSS — verdict UPGRADE-READY,
because node is Maintenance and bun loaded within the 10% regression
allowance. These numbers are that machine's only; re-running produces
new numbers, because the numbers are measurements.

Demo: runverdict runtimes inventory and measure verdict on the build machine

Limits, stated plainly

  • A load check proves the entry loads, not full compatibility. A passing load check is not a guarantee the app behaves identically on another runtime.
  • Numbers are this machine's. A faster runtime here is not a claim about any other machine, app, or workload.
  • Node 26 is EOL-table-only in v1 until it is measured live on a machine that has it installed. The tool reports not-installed runtimes as skipped, never downloads them.
  • It never downloads runtimes, never touches the network beyond localhost, and never reads files outside the app directory except the runtime binaries.

Where rivals beat us

  • clinic.js beats us on profiling depth: flame graphs and event-loop diagnosis. It profiles one runtime's bottleneck; it has no cross-runtime comparison, no EOL schedule, no upgrade verdict.
  • hyperfine beats us on statistical rigor: outlier detection and warmup modeling. It is a generic timer; it knows nothing about JS runtimes, EOL lines, native addons, or upgrade risk.
  • autocannon beats us on HTTP load options. It gives HTTP numbers only; no runtime awareness, no verdict, no EOL, no addon/load check.

v1 does none of those deeply. It does the upgrade decision, which none
of them do.

The code is on GitHub under MIT, zero runtime dependencies:
https://github.com/techdevfun/runverdict

Top comments (0)