Glossary · Structure and dead code

What is a zombie dependency?

A zombie dependency is a package that a project declares or ships but nothing imports. It is still installed, updated and scanned for vulnerabilities, yet no code uses it. The term covers unused entries in a manifest such as package.json, and internal monorepo packages that no other package depends on.

Last reviewed: November 2026

Also called Unused dependency, Zombie package, Phantom dependency

How a zombie dependency is measured

The method is a set difference: list what the manifest declares, list what the code actually imports, and report the declared packages with no importer.

Declared in manifestImported by codeVerdict
lodash, axios, left-padlodash, axiosleft-pad is a zombie

The check has two known blind spots. A dependency used without an import statement (a CLI run from a script, a plugin loaded by name from config, a type-only package) looks unused. The reverse problem, an import of a package missing from the manifest (a phantom dependency), is found by the same tools.

Three things get confused with it:

  • Unused import: one statement in one file. Linters catch it.
  • Abandoned upstream package: unmaintained but possibly in heavy use.
  • Zombie dependency: declared or shipped, imported by nothing.

Why zombie dependencies matter

An unused dependency costs something every time the project is built or scanned. It is downloaded in CI, included in container images, and listed in vulnerability reports, so a CVE in a package you never call still creates an alert someone has to triage. In a monorepo the same problem exists one level up: an internal package that nothing depends on keeps being built, tested and kept in sync with shared tooling. Removing it is cheaper than maintaining it, but only after you confirm it is really unused.

How to measure zombie dependencies yourself

Use the checker native to your ecosystem:

  • JavaScript and TypeScript: npx knip lists unused dependencies along with unused files and exports; npx depcheck is the narrower single-purpose tool.
  • Python: deptry . compares pyproject.toml or requirements.txt against imports and reports unused, missing and transitive dependencies.
  • Rust: cargo machete is fast and heuristic (text matching); cargo +nightly udeps is slower and more accurate because it uses the compiler.
  • Go: go mod tidy removes requirements the module no longer needs.

Pick one of these over any cross-language tool when you are checking a package manifest: they know their ecosystem's naming quirks, such as a Python distribution name differing from its import name.

How repowise measures zombie dependencies

repowise detects the internal-package case. In its dead code analysis, a zombie_package is a top-level directory that declares itself a package (it holds its own manifest) and has no import edges coming in from files in any other top-level directory. Third-party imports do not count as importers; a framework wiring anchor does.

The detector skips directories that are not packages: tooling and docs folders, directories with no manifest of their own, packages that ship a runnable program (a CLI entry, a JVM main), and folders with only config or data. It needs at least two top-level packages to compare.

Zombie packages get a fixed confidence of 0.5 and arrive as review findings, because a package can be consumed by a deploy script or another repository. Each finding carries line count, last commit, 90-day commit count and the most common owner, so you can tell a dormant package from one under active work.

  • CLI: repowise dead-code --kind zombie_package; --no-include-zombie-packages turns the pass off.
  • MCP: get_dead_code, one of the 10 repowise MCP tools.
  • Manifest entries: knip, deptry and cargo-machete (above) diff a manifest against imports, and pair well with repowise's repository-wide view of zombie packages.

See dependency graph for how the import edges are built.

FREQUENTLY ASKED

Zombie dependency: questions

What is the difference between a zombie dependency and an unused import?

An unused import is one line in one file that brings in a name nothing uses. A zombie dependency is a whole package kept in the manifest (or a whole internal package) that no file imports. Removing every unused import does not always remove the dependency, and the reverse.

Is a zombie dependency the same as an abandoned package?

No. An abandoned package is unmaintained upstream but may be used heavily. A zombie dependency is unused locally but may be actively maintained. A package can be both, and each is worth fixing for a different reason.

How do you find unused dependencies?

Run the checker for your ecosystem: knip or depcheck for npm, deptry for Python, cargo-machete or cargo-udeps for Rust, go mod tidy for Go. Then check the list for packages loaded by name at runtime, such as plugins and CLI tools, which these tools can miss.

Why are unused dependencies a problem?

Each one adds install time, image size and supply-chain surface, and each can raise vulnerability alerts for code you never run. They also make it harder to see what the project really depends on.

See it on your own repository.

Index a repository free and read the result for your own code and history. Rolling it out to a team? We will walk you through it.