Your node_modules Folder Is a Liability: How Smart Teams Cut Package Bloat by Nearly Half
The Folder Nobody Wants to Look At
Every JavaScript developer knows the joke: node_modules is the heaviest object in the known universe. It's funny until you're staring at a 45-second CI build, a Docker image pushing 2GB, or a security audit flagging 17 transitive vulnerabilities from a package you installed four years ago and completely forgot about.
Dependency bloat is one of those problems that sneaks up on teams. You install something for a quick prototype, the prototype ships, and suddenly that package — along with its 47 dependencies — is riding along to production forever. Multiply that pattern across 18 months of active development and you've got a real mess.
We talked with DevOps engineers at several mid-size US tech companies who've gone through serious dependency audits. The numbers they shared were hard to ignore: teams routinely trimmed 35–45% of their installed packages without losing a single feature. Here's what they learned.
Why Bloat Happens (And Why It Compounds)
The root cause is almost never malicious — it's just convenience. npm makes installing packages frictionless by design. That's a feature, not a bug. But frictionless installation doesn't come with frictionless accountability.
A few common patterns the engineers we interviewed kept citing:
- "Left-in" dev dependencies that somehow made it into production bundles
- Overlapping packages doing the same job (two different date libraries, three HTTP clients)
- Abandoned packages that were replaced internally but never removed from
package.json - Deep transitive trees where one top-level package drags in 30 sub-dependencies, half of which are redundant
Jordan, a senior DevOps engineer at a SaaS company in Austin, put it bluntly: "We had moment.js and date-fns and dayjs all in the same codebase. Nobody remembered why. We were shipping three date libraries to users who needed exactly one."
The Audit Toolkit: What Actually Works
Before you start nuking packages, you need visibility. These are the tools teams found most effective:
depcheck
depcheck scans your codebase and flags packages listed in package.json that aren't actually referenced anywhere in your source files. It's not perfect — it can miss dynamic requires — but it's a fast first pass that almost always surfaces obvious dead weight.
Run it, save the output, and treat it as your starting hit list.
npm ls and why-is-it-installed
npm ls <package-name> shows you the full dependency tree for any given package. Pair it with npm why (or the Yarn equivalent) to understand why something is installed. Often you'll find that a package you thought you needed directly is actually a transitive dependency of something else — which means removing the parent clears both.
Bundlephobia and bundlesize
For frontend-heavy projects, Bundlephobia is invaluable. Paste in a package name and you'll see its minified + gzipped size, its dependency count, and whether it supports tree shaking. This is where teams often have a come-to-Jesus moment about what they've been shipping to end users.
webpack-bundle-analyzer / Vite's rollup-plugin-visualizer
If you want to see where your bundle weight is actually coming from, these visual tools are worth every second of setup time. You get a treemap of your entire bundle, color-coded by module. Patterns jump out immediately.
A Decision Framework That Doesn't Require a Committee
One engineering manager at a fintech startup in Chicago described a simple three-question test her team now applies to every package, both new installs and existing ones during audits:
- Is it actively maintained? Check the GitHub repo. Last commit more than 18 months ago with open CVEs? That's a red flag.
- Can we replace it with native code? JavaScript's standard library has grown significantly. A lot of packages that were necessary in 2018 —
isarray,left-pad,object-assign— are genuinely redundant now. - Is the functionality we actually use worth the full package weight? If you're using one function from a 40KB library, consider copying that function directly or finding a lighter alternative.
This framework sounds obvious, but teams told us that just having an explicit process changed behavior. Developers started asking the questions proactively instead of defaulting to install.
Real Before/After Numbers
We asked teams to share concrete metrics from their audits. A few highlights (companies asked to remain anonymous):
- E-commerce platform, ~80 engineers: Reduced
node_modulespackage count from 1,247 to 703. Docker image size dropped from 1.8GB to 1.1GB. Average CI pipeline time went from 6:40 to 4:10. - B2B SaaS, ~30 engineers: Removed 89 packages flagged by
depcheck, eliminated 4 overlapping utility libraries. Reported 22% improvement in local dev startup time. Security audit findings dropped by roughly 30%. - Media startup, ~15 engineers: Replaced
moment.js(with its locale files) withdate-fns. Frontend bundle shrank by ~67KB gzipped. No user-facing changes whatsoever.
The security angle is worth emphasizing. Every package in your dependency tree is a potential vector. Fewer packages means fewer CVEs to triage, fewer npm audit warnings to ignore, and a smaller blast radius if something does get compromised. In a world where supply chain attacks are increasingly common, that's not a minor benefit.
Making It Stick: Preventing Future Bloat
Auditing once is good. Building systems that prevent re-accumulation is better.
Several teams mentioned adding depcheck to their CI pipelines — not as a hard blocker initially, but as a reporting step that posts results to Slack. Visibility alone changed habits.
Others implemented lightweight PR review checklists that include a question about new dependencies: "Is this package necessary? Is there a native alternative? Is it actively maintained?" Low friction, high signal.
One team started tracking total package count as a metric alongside build time and test coverage. It sounds almost silly, but treating it as a number you're collectively responsible for shifted how developers thought about installs.
The Bigger Picture
Dependency hygiene isn't glamorous work. There's no launch post for "we removed 400 packages." But the compounding benefits — faster builds, leaner deployments, cleaner security posture, lower cognitive overhead — are real and durable.
The teams that did this work consistently said the same thing afterward: they wished they'd done it sooner. The audit felt daunting before they started. Once they had the right tools and a clear process, most of the heavy lifting was done in a few focused sprints.
Your node_modules folder doesn't have to be the heaviest thing in the universe. Start with depcheck, pick the low-hanging fruit, and build from there. Ship less. Ship faster. Your future self will thank you.