Your package manager has a blind spot: it can tell you if a dependency is broken, but not how long ago it was written.

npm audit answers whether anything is known to be broken. It says nothing about whether the code is being looked after. Those are different questions, and only one of them has a command.

We measured the second one across 302 packages — the exact versions in this site's lockfile, not their latest releases, because a package can look alive while you are pinned to something from years ago.

The numbers

Median age of the installed version286 days (0.8 years)
Packages with no release of any version in two years65 of 302 — 21.5%
Oldest installed version[email protected], 11.2 years

The median is healthier than we expected. Half the tree is under ten months old, which reflects a build toolchain that moves quickly and gets updated.

The tail is the interesting part. More than one package in five has seen no release at all in two years, and the oldest things in here predate the framework they are installed alongside.

PackageInstalled versionAge
require-directory2.1.111.2 years
codemirror-spell-checker1.1.210.2 years
rw1.3.39.6 years
bcryptjs2.4.39.5 years

Old is not the same as bad, and the distinction is the whole guide

Don't read that table as an indictment. Treating age as a fault leads to worse decisions than ignoring the data entirely.

require-directory does one thing: it reads a directory and requires the files in it. That problem has not changed since 2015. The package has needed no release because there has been nothing to release. Eleven years without a commit is, for a utility of that size, a sign that it was finished.

The industry reflex is to treat "unmaintained" as "dangerous," which mistakes activity for health. A package that ships every month may be churning because it keeps breaking. A package that has not shipped since 2016 may simply be correct.

Age is a question, not a verdict. What it tells you is where to look, and the useful reading is a pair of questions per package: is the problem it solves still stable, and if something were wrong with it, would anyone fix it?

Where Age Matters

Three cases where the number should change what you do.

Anything security-relevant. bcryptjs at 9.5 years is the entry in our own list that deserves a look — not because a password hashing implementation rots, but because the surrounding advice does: parameters that were adequate in 2016 are not adequate now, and a package that has not shipped since then has not updated its defaults or its guidance. The code being stable is not the same as the recommendation being current.

Anything touching a moving external contract. Parsers, protocol clients, format readers. If the thing on the other end changed and the package did not, the package is wrong even though nothing about it decayed.

Anything you would need fixed quickly. The real cost of an unmaintained dependency isn't its age, but the response time when something goes wrong. A package with no releases in two years has, in effect, no incident response, and you inherit that.

For everything else — small utilities solving settled problems — the age is a curiosity.

The median is the wrong number to quote

We led with 286 days because it is the summary people ask for, and it is close to useless on its own.

A dependency tree isn't a normal distribution. It's two different populations in one coat: fast-moving build tooling that updates constantly, and small utilities that were finished years ago. The median lands in the gap between them and describes neither.

Ours is 0.8 years while the oldest entry is 11.2 — a spread of more than an order of magnitude. Quoting the median would suggest a tree that is uniformly under a year old, when what actually exists is a modern toolchain sitting on top of a layer of settled utilities.

The number that changes decisions is the 21.5% with no release in two years, because that is a list of packages rather than a statistic, and a list can be read. When you run this on your own tree, sort it and look at the tail. The average tells you nothing you can act on.

Check your own tree

The registry publishes a full release history:

npm view lodash time --json

Two figures are worth pulling out for each package. When was the version you installed published, which tells you how far behind you are, and when did the package last release anything, which tells you whether it is being looked after at all. They answer different questions and a package can score well on one and badly on the other.

Then sort by the second and read the top of the list against the three cases above. On a tree this size that is a few minutes of work, and it produces a list you can actually act on rather than a number to feel bad about.

Limits

This analysis is based only on publication dates. It can't tell you if a package is maintained in the ways that matter: is someone reading the issues? Would a security report get a response? A package can have shipped last week and be effectively abandoned; another can be years old and have an attentive maintainer who has simply had nothing to change.

It also measures one project's tree, on one day. A different application would produce a different distribution, and ours skews modern because much of it is recent build tooling. The 21.5% figure is ours, not the ecosystem's.

And it deliberately does not overlap with npm audit. Whether any of these packages has a known vulnerability is a separate question with an existing answer, and nothing here should be read as a substitute for asking it. The companion guides on what a package signature proves and the licences in your tree cover the other two things a lockfile knows and does not tell you.

Figures are from 19 August 2026, resolved for the exact installed versions; the script is in our repository and points at any package-lock.json.