A package can receive tens of millions of downloads a month after its maintainers have deprecated it. Another can remain actively maintained while no longer being necessary for a new project to accomplish the same task. npm download counts alone make both changes difficult to see.
Which libraries and utilities are fading, and which deserve to stay? A list of supposedly obsolete packages cannot answer that question. The useful question is: Does the reason we installed this dependency still exist? If it does, what responsibility is the package carrying, and for whom?
My argument is that tools are most vulnerable when they fill a platform gap without continuing to handle additional complexity. That does not mean small packages should all be removed, or that native APIs are always better. Drawing on official documentation, maintainer statements, and npm data checked on October 8, 2026, this article examines five kinds of replacement pressure, separating confirmed conditions from my interpretation.
Downloads measure traffic, not deliberate choices
Request's README says it has been deprecated since February 11, 2020. inflight's package metadata says it is unsupported and warns of a memory leak. Moment is different: it remains maintained, but expanding its feature set is not the goal. Calling all three “dead packages” would erase a meaningful distinction. Request statement, inflight metadata, Moment's current policy.
I queried npm's official Downloads API for the complete calendar month of September 2026. These three cases were deliberately selected to test whether a maintenance or deprecation signal means downloads disappear. They are neither a random sample nor a measurement of ecosystem market share.
Deprecation and maintenance mode can coexist with substantial downloads
All three packages still received substantial downloads in September 2026. These bars do not measure new-project adoption.
- request (deprecated)59.699
- moment (maintenance mode)149.03
- inflight (deprecated)392.617
View npm download data
| Package and maintenance status | million downloads (approx.) / September 2026 |
|---|---|
| request (deprecated) | 59.699 |
| moment (maintenance mode) | 149.03 |
| inflight (deprecated) | 392.617 |
Official npm API, September 1–30, 2026, inclusive UTC dates. Values are in millions, rounded to three decimal places; the source retains exact counts. This is a one-month snapshot, not evidence of growth or decline. Downloads are not people or projects; the data cannot distinguish new adoption, transitive dependencies, or automated installations, or quantify their respective contributions.
SourceThe API returns downloads for a period. It has no field indicating whether an engineer deliberately selected the package. Consider a hypothetical old tool that still depends on Request: rebuilding without a cache can download it again without anyone reconsidering its design. That is an example of a possible mechanism, not a measured explanation of the totals above. npm API and date definitions.
An investigation therefore needs four separate questions: Is the capability still scarce? What do maintainers still promise? Do new projects choose it? Can existing systems migrate away? A deprecation statement answers part of the second; comparing APIs helps with the first. A monthly download total cannot settle the last two.
That is the boundary of this article. The evidence supports discussing uses that are losing their necessity. It does not establish declining new-project adoption across all five categories.
1. Compatibility layers that supply a standard capability
The original purpose of node-fetch is clear: bring the Fetch API to Node.js. Once the runtime provides a stable fetch, installing another package solely to make a basic HTTP request becomes harder to justify. Node documents Fetch as no longer experimental from v21; the capability baseline used here is the Node 24 documentation. Node.js Fetch.
This is documented absorption by the platform, not proof that node-fetch usage is falling. Nor does it mean migration consists of deleting an import. node-fetch uses Node streams for response bodies, while native Fetch uses Web Streams. Its agent option is also a different interface from the dispatcher used by Node's native Fetch. Stream consumption, connection configuration, and cancellation need comparison against actual application behavior. node-fetch documentation.
An HTTP client's remaining value may sit above transport. Shared error classification, retry policies, authentication, and observability can still justify an abstraction. Native fetch does not automatically reject its Promise for every HTTP error status; the application must decide how to handle responses. Retrying a write request also requires thinking about duplicate execution, not merely adding a loop. Fetch Standard.
There is a further wrinkle: Node's Fetch implementation is based on Undici. Removing an explicitly installed package does not necessarily remove the underlying maintenance work. The runtime may have taken it over. What is fading is a reason to install something separately, not necessarily the value of the engineering underneath it.
2. Toolboxes that make everyday syntax more convenient
Finding array elements, iterating, and removing duplicates were useful entry points into general-purpose utility libraries. Today, Array.prototype.find, map, and Set are language features. If a project only needs to deduplicate an array of simple strings, an additional dependency has less work to justify. ECMAScript arrays and collections.
But similar-looking code is not necessarily equivalent. This deliberately constructed counterexample shows the problem:
const settings = { retries: null };
// _.get uses its default only when the result is undefined.
_.get(settings, 'retries', 3); // null
// ?? applies its default to both null and undefined.
settings?.retries ?? 3; // 3If null means “explicitly disabled,” a mechanical replacement changes a business rule. Lodash's documentation defines the first contract. Auditing a utility should begin with the behavior callers rely on, rather than a comparison of character counts. Lodash get.
Similarly, structuredClone handles many data types and circular references, but it is not a transparent copy operation for arbitrary JavaScript objects. Functions, DOM nodes, prototypes, and property descriptors introduce boundaries. Replacing every cloneDeep call without examining the values is not a migration strategy. Structured clone algorithm.
Debouncing also means more than “run this later.” Lodash's debounce exposes leading, trailing, maxWait, cancel, and flush behavior. If the application depends on those interactions, a ten-line local implementation may not be cheaper. Lodash debounce.
The pressure falls on the parts of a toolbox that overlap with native capabilities. It does not prove that Lodash as a whole has lost its value. One library can contain both easily replaced conveniences and contracts worth retaining. The unit of replacement should be a use case; removing the entire package need not be the starting objective.
3. Utilities that fill gaps in Node's filesystem APIs
Creating nested directories, copying directory trees, and matching paths once commonly called for additional tools. Node 24 includes recursive mkdir, fs.cp, and fs.glob. cp ceased being experimental in v22.3, and glob became stable in v24.0. These are worth checking first when writing a new script with a fixed runtime. Node.js filesystem documentation.
This hypothetical build-script fragment illustrates what Node 24 can do directly. It does not claim equivalence with every third-party filesystem tool:
import { mkdir, cp, glob } from 'node:fs/promises';
await mkdir('dist/assets', { recursive: true });
await cp('assets', 'dist/assets', { recursive: true });
for await (const file of glob('content/**/*.mdx')) {
console.log(file);
}An application can prescribe a Node version. A public library may need to support the minimum versions used by its consumers. Those are different decision environments. Even where versions align, compare symbolic links, overwriting, exclusion rules, hidden files, and platform-specific paths. Identical API names do not make every third-party glob option equivalent.
There are three gates to retirement: the platform must provide the capability, the project must be able to require a compatible environment, and migration must cost less than retention. Passing the first gate says nothing automatic about the other two. Many “obsolete dependencies” remain because support commitments and risk have not moved with the API surface.
4. Wrappers that preserve contracts from an earlier architecture
Request and Moment are often grouped together, but they represent different maintenance decisions. Request is deprecated. Moment continues maintenance and preserves its mutable-object API. Saying only that newer alternatives exist misses what makes older systems difficult to move.
Moment revised its policy on August 17, 2026. It still does not accept new user-facing features, but technical maintenance remains possible, and a breaking maintenance release numbered 3.0 is no longer categorically excluded. The old announcement's “no v3” position should not be presented as its current commitment. Moment policy update.
Migration costs live in contracts. Imagine a date workflow that relies on an operation mutating its input. A replacement that returns a new object might show a plausible date on screen while leaving another part of the workflow reading a stale value. Before replacing method names, inventory mutation, parsing, time zones, and formatted output.
Native Intl.DateTimeFormat can handle many display requirements. Formatting an instant, however, does not parse every human input or define rules such as “the same day next month” and appointments across daylight-saving transitions. ECMA-402 date formatting.
Request users may likewise rely on cookie, proxy, or streaming settings. Start with the contracts the application actually uses, then choose a replacement client or a thin adapter. Temporarily retaining a dependency can be an engineering decision with an owner and a deadline. “Stop adding new uses” and “remove it everywhere today” are different instructions.
5. Browser helpers for work the browser now performs
Some introductory jQuery use cases can now be handled by querySelectorAll, classList, and addEventListener. Intersection Observer provides a foundation for detecting when elements enter the viewport. If these tasks comprise a package's entire value, native capabilities do reduce its necessity. DOM Standard, Intersection Observer specification.
Yet jQuery released version 4.0 on January 17, 2026. That directly contradicts the inference that “new projects may not need it” means “the project is no longer maintained.” Existing plugins, integrations, and team familiarity can still be reasons to keep it. jQuery 4.0 announcement.
Native building blocks are not complete products. Observing intersections does not finish image-loading failure handling, virtualized lists, focus order, or screen-reader interactions. When a framework or higher-level component takes on those responsibilities, an application may stop directly installing a utility while its work continues inside an integration layer.
| Category and original purpose | Capability taking over | Remaining needs to check |
|---|---|---|
| Fetch compatibility: bring Fetch to Node | Runtime-provided Fetch | Streams, connection configuration, minimum versions |
| General toolbox: simplify data operations | Array methods, Set, optional chaining | Edge semantics, deep data, debounce contracts |
| Filesystem tools: directories, copying, matching | Node mkdir, cp, glob | Paths, links, exclusions, supported versions |
| Older API wrappers: unify HTTP or date operations | New clients, Intl, other date tools | Mutation, parsing, time zones, compatibility promises |
| Browser helpers: selection, events, visibility | DOM and observer APIs | Plugins, integrations, complete interaction behavior |
This is a map of replacement boundaries, not a ranking of decline. Every row may include actively maintained products with continuing demand.
AI can replace thin wrappers—and prolong old dependencies
The following is my interpretation of the mechanism. If a tool wraps a few stable APIs, with clear requirements, controlled inputs, and behavior that is easy to verify, cheaper AI-assisted implementation may make an external dependency less attractive. A team can own a small amount of local code instead of accepting the package's complete interface and upgrade cadence.
Generating code does not eliminate responsibility. Edge cases, tests, security fixes, and version compatibility formerly handled by maintainers move into your repository. The easier it becomes to generate an implementation, the more useful it is to ask who can recognize when that implementation is wrong.
Maintainers have also observed pressure in the opposite direction. Moment's 2026 announcement suggests that agents' familiarity with existing code and documentation may be encouraging new use. That is the team's interpretation of growth, not an established causal finding about AI. The download data collected here cannot test it. Moment's observations about agent use.
“AI will make utilities disappear” is therefore not a sufficient conclusion. It may make simple implementations easier to own, while examples and older knowledge repeatedly introduce the same dependencies. Historical visibility could even influence automated selection more than suitability for the current environment. That is a risk to manage, not a market trend quantified by this investigation.
For library authors, I would prioritize verifiable correctness, explicit support boundaries, reliable documentation, and tests for specialist failure cases. A thin convenience layer is easy to reproduce. Years spent discovering how a problem fails are not as cheap.
What deserves to stay? Examine the complexity it continues to carry
Time-zone data changes with political decisions. IANA explicitly describes updates reflecting changes in UTC offsets, zone boundaries, and daylight-saving rules. That work does not end because the first implementation took only a few functions. IANA Time Zone Database.
Parsers, cryptographic implementations, complex protocols, and accessible components suggest the same evaluation principle. The real cost may lie in tracking specifications, handling hostile or unexpected inputs, testing across environments, and correcting mistakes. These are responsibilities to investigate when selecting dependencies, not a security endorsement of any particular package.
A tool containing only a few dozen lines may encapsulate subtle behavior you do not want to own. A large toolbox may be used in your project for a single native operation. Size has no fixed relationship to value. The question is whether removing the dependency transfers its remaining work to the platform or quietly hands it back to your team.
I would turn that assessment into the following actions, without making zero dependencies the objective:
| Current situation | Action | Acceptance criteria |
|---|---|---|
| Native support is sufficient, runtime fixed, use simple | Gradually replace the direct dependency | Verify successful and failing behavior with real inputs |
| Native capability is close but semantics differ | Retain it first, or introduce a thin compatibility layer | Check nulls, errors, cancellation, time zones, and other contracts |
| A deprecated package is used directly | Stop expanding usage and schedule migration | Assign an owner, replacement, and rollback point |
| A deprecated package arrives transitively | Trace and upgrade or replace the parent dependency | Do not treat manual lockfile edits as a root-cause fix |
| A dependency handles a changing specialist problem | Retain and reassess it | Examine maintenance commitments, tests, support, and update capacity |
In practice, use the package manager's dependency tracing first, then search for the functions the application actually calls. Record minimum runtime versions and important semantics. Replace one small path, compare success, failure, and boundary inputs against existing behavior tests, and inspect the production build and dependency tree. Removing a direct declaration may leave another copy brought in upstream. Deleting the lockfile does not teach that upstream package a new API.
Return to the package still accumulating downloads. It may be essential infrastructure, or a trace of migration that has not happened. The number alone cannot decide.
The retirement signal worth tracking is that the work a package used to do now has a more appropriate, verifiable owner. As platforms absorb basic capabilities, tools need deeper expertise to keep earning their place. When teams choose to own implementations, they also inherit the responsibilities they once outsourced. One fewer installed package is an outcome. Knowing who handles the remaining complexity is the decision.
