dead-deps — what happened to your dependencies

How dead-deps decides a package is unmaintained

Every verdict is a state with sources attached, not a score with a threshold. This page describes what gets read, how it is weighed, what the tool refuses to claim, and the guard that keeps finished packages out of the report.

What gets read

Nothing is scraped and nothing is guessed. For each dependency the tool asks public indexes for a fixed set of facts: the registry’s own status and any deprecation message, the repository URL and whether it is archived, first and latest release dates, total version count, the median gap between releases across the package’s life, dependent counts, downloads, the development distribution score, committer count, issue and pull-request activity over the trailing year, the list of maintainers upstream still considers active, and any open advisories.

Responses are cached on disk for a day by default, so a repeated scan costs nothing upstream. Sources that fail are recorded as failures rather than treated as zeroes — a network error must never read as "no activity".

Every claim carries its source

Each signal that argues for or against a verdict becomes a piece of evidence: a one-line human-readable label, a URL where you can check it, when the datum was observed upstream, and a signed weight. Negative weights argue that the package is alive. An unsourced verdict is treated as a bug in the tool, not as a judgement call.

Kinds of evidence a verdict can rest on.
KindWhat it means
registry-deprecationAn explicit deprecation notice on the npm registry.
repo-archivedThe source repository is archived and read-only.
repo-missingThe repository the package points at no longer resolves.
release-cadenceThe gap since the last release, measured against the package’s own median gap, not a fixed threshold.
commit-activityWhen the repository was last pushed to.
issue-responsivenessIssues and pull requests opened, closed and merged over the trailing year.
bus-factorHow concentrated authorship is; a development distribution score near zero means one person.
maintainer-activityWhether upstream still considers any maintainer active.
security-advisoryOpen advisories against the package.
dependent-flightDependents moving away faster than they arrive.
curated-datasetA hand-verified row in the succession dataset.
stability-heuristicSignals that argue the package is finished rather than abandoned. These carry negative weight.

The verdict is a state, never a boolean

"Is this package dead" is the wrong question, because the interesting cases are the ones where the honest answer is "it is finished", "it is slowing down", or "we do not know". The tool sorts findings and sets its exit code by severity, so a CI gate can be pointed at exactly the states you care about with --min-state.

Maintenance states, mildest first. Severity is the sort key and the --min-state ordering.
StateSeverityMeaning
active0Releases, commits or issue responses within the recent window. Nothing to do.
stable-complete1Quiet, but finished rather than abandoned: small surface, no open bug pressure, still depended on heavily and still working. Never reported as a problem.
unknown2Not enough coverage to judge — an unindexed package, a private registry, or a source that failed. Reported as ignorance, not as a verdict.
low-activity3Slower than its own history would predict, with some sign of life. Worth watching, not worth a migration.
unmaintained4The release cadence has stopped relative to its own baseline, issues go unanswered, and no maintainer is visibly active.
deprecated5The registry or the maintainers say so explicitly. This is a statement of fact from upstream, not an inference.
abandoned6Unmaintained, plus a hard signal: an archived repository, a missing repository, or a maintainer statement that the project is over.
hijack-risk7Abandoned while still widely depended on, with a small bus factor or an unclaimed publish surface. The supply-chain case, ranked worst on purpose.

Why "quiet" is not "dead"

The naive detector reads the date of the last release and calls anything old dead. It scores well on obviously dead projects and is still worthless, because a large share of the npm graph is tiny packages that were finished years ago. once, wrappy, inherits, util-deprecate, imurmurhash: they do one small thing, they do it correctly, there is nothing left to add, and so nobody commits to them. They are downloaded hundreds of millions of times a week.

On last-release date alone those packages are indistinguishable from abandonware. Flagging them is not a harmless false positive — it is the failure mode that makes a maintenance tool useless, because a report full of things you must not act on trains you to ignore the report. Recommending that someone migrate off inherits has not found a problem; it has become one.

The stable-complete guard

So "finished" is a first-class verdict with its own guard, and the guard runs before anything is reported. A package is treated as stable-complete rather than unmaintained when the picture is coherent: a long release history that settled down rather than stopped mid-flight; a small, stable API surface; no deprecation notice and no archived repository; no open advisories; no pile-up of unanswered issues relative to the package’s size; and continued heavy use by dependents that have had every opportunity to leave and have not.

The guard is deliberately asymmetric. Any hard signal — a registry deprecation, an archived repository, an open advisory, a maintainer saying the project is over — overrides it immediately, because those are statements of fact rather than inferences from silence. Silence alone can only ever produce stable-complete or low-activity; it can never on its own produce abandoned.

Confidence, and admitting ignorance

Upstream indexes lag. A verdict built on two-year-old issue data has to say so rather than presenting it as current, so every assessment carries a confidence of high, medium or low derived from how much of the signal set was actually available and how fresh it was. When coverage is too thin to judge, the state is unknown and the package is not reported as a problem. "We could not find out" and "it is fine" are different answers and the tool keeps them apart.

Where successors come from

What replaced a package cannot be inferred from metadata, so it is not. The successor half of every report comes from a hand-curated dataset in which each row is checked by a human against primary sources: an npm deprecation notice, a maintainer statement, an archived repository, a README, a release note, or an official migration guide. Blog posts are supporting evidence, never the only evidence.

A row is only added when the succession is consensus rather than opinion. Where reasonable engineers still disagree, the row is marked low confidence and the candidates are listed as alternatives instead of one being crowned. Rows about small, finished, still-working packages are refused outright — the dataset is a map of successions, not a list of packages someone finds unfashionable.

Each row also records how the succession happened, because "a maintained fork exists" and "the platform absorbed it" call for completely different work. All 81 rows are published as individual pages with their evidence.

Calibration

Because the false-positive claim above is the whole value of the tool, it is measured rather than asserted. A hand-labelled corpus of real packages — deliberately adversarial, and weighted toward finished-but-quiet ones — is scored against the detector, and the false-positive rate over the stable-complete bucket is reported separately from overall accuracy. A detector that flags finished packages fails calibration no matter how well it does elsewhere.

MetricValueBasis
False positives on finished packages0.0%0 / 13 stable-complete
False alarms on anything healthy0.0%0 / 30 healthy packages
Missed dead packages4.2%1 / 24 genuinely dead
Strict accuracy (exact state)83.3%45 / 54
Lenient accuracy (near misses forgiven)87.0%47 / 54

Corpus: 54 hand-labelled packages, measured on 2026-08-04. The detector is scored on upstream signals alone here — the curated succession dataset is deliberately withheld from it, so these numbers are not inflated by answers it was handed.

What this cannot tell you

See it applied

Back to the full package index.