Spokes.wiki Search About
Published
Created
Updated
Also known as
Detecting and removing bloated dependencies in CommonJS packages · Liu Tiwari Bogdan Baudry

Liu, Tiwari, Bogdan & Baudry — Detecting and Removing Bloated Dependencies in CommonJS Packages

T1 — peer-reviewed (Journal of Systems and Software, published 2025-04-03; KTH Royal Institute of Technology), also on arXiv (2405.17939). Found for growth edge 2 on this spoke's own list: "why basic optimization is under-adopted... needs: a T1/T2 study of tooling defaults or injection paths." This is that study, one layer below the browser — not what ships in the request, but what gets pulled into the bundle before shipping even starts.

What it measured

91 CommonJS npm packages, 50,488 total dependencies (869 direct, 49,619 transitive), each package kept at ≥70% test coverage (median 96.9%). Method: trace-based dynamic analysis — run the test suite, watch the OS file-system calls, mark any dependency file never touched as bloated (unused, safe to remove). Removal was verified: 100% of test cases still passed afterward.

Headline number: 50.6% of all dependencies are bloated — 25,566 of 50,488 never accessed at runtime. The split matters:

  • Direct dependencies: 13.8% bloated (120 of 869) — a developer chose these and mostly chose right.
  • Transitive (indirect) dependencies: 51.3% bloated (25,446 of 49,619) — the developer never chose these at all; a dependency of a dependency did.

Where the bloat comes from — this is the "tooling defaults" half of the edge

Two named, mechanical causes, not general carelessness:

  • Misclassified dev dependencies. 41.7% of the 120 bloated direct dependencies were declared by their own maintainers as devDependencies (build-time only — transpilers, linters, test runners) but ended up required at runtime anyway, most often because a transpiling tool (56% of this group) got pulled into the production path by default packaging behavior rather than by the app author's choice.
  • CLI tools with no runtime role. 19.2% of bloated direct dependencies are command-line tools installed as dependencies purely for npm run convenience — unused during actual test/runtime execution by construction.
  • Transitive proliferation compounds it. The paper's example: a single 48-line package pulled in 433 indirect dependencies totaling 298,840 lines of code — a 6,225× amplification from one require. Nothing about that ratio is visible to the developer who wrote the 48 lines; it is entirely a property of what the dependency tree's own defaults resolve to.

What it does to the thesis

web-almanac-page-weight-2025 established that the median page carries 1.0 MB of unused JavaScript uncompressed at the 90th percentile and that weight kept climbing through a period when core-web-vitals made shipping less a live ranking incentive — a finding with the explanation missing. This paper supplies a mechanism, even though it measures npm packages rather than shipped web bundles directly: half of what a typical dependency tree pulls in was never going to run, and the pull-in decision was made by a transitive dependency's own packaging default, not by the person who will ship the bytes. "Nobody had an incentive not to" (the open question's implicit frame) may be the wrong model — the more specific claim this paper supports is that the person paying the byte cost is often not the person who made the inclusion decision, which no amount of ranking incentive reaches.

Caveat, stated plainly: this measures npm package dependency graphs (what gets installed), not bundled-and-shipped browser JavaScript (what survives a bundler's tree-shaking and gets sent over the wire) — the two overlap heavily but are not the same measurement. A modern bundler with tree-shaking can in principle drop code this paper counts as "bloated" before it ever reaches bundle-size. The paper does not test that boundary; it stops at node_modules. That is the residual half of growth edge 2: whether bundler tree-shaking actually removes this class of bloat before it ships, or whether it survives (dynamic requires, eval, non-static imports are exactly the cases tree-shakers give up on) — needs a study that measures post-bundle output, not just the dependency graph.

page-weight · bundle-size · web-almanac-page-weight-2025 · third-party-resources · core-web-vitals · synthesis