You Don't Need That npm Package: The Real Cost of Dependency Bloat — Data, Case Studies, and Practical Principles
13 min read
npm has over 2.3 million packages — the largest software registry in history. The average Node.js project has 83 direct dependencies, each with their own dependencies. The left-pad incident of 2016 broke thousands of apps worldwide. Dependency bloat is a real, measurable risk — and most developers underestimate it.
npm (Node Package Manager) has over 2.3 million packages as of 2024, making it the largest software registry in history. The average Node.js project installs 83 direct dependencies — and each of those dependencies has its own dependencies. A typical modern JavaScript application, when all transitive dependencies are counted, may include hundreds or even thousands of packages. This ecosystem is the foundation of modern web development's productivity. It is also one of its most underappreciated risks.
The dependency problem became viscerally real on March 22, 2016, when Azer Koçulu unpublished a small npm package called 'left-pad' — an 11-line utility that padded strings with spaces or zeros — after a dispute with npm over a naming conflict. Within hours, thousands of projects using Babel, React, and other widely-used packages broke in production. Facebook, Netflix, Airbnb, and countless development teams worldwide were affected. The incident revealed that the global software infrastructure had a single point of failure in an 11-line package that any competent developer could have written in 5 minutes.
The Scale of the Problem: What the Data Shows
The Socket Security research team — which continuously analyzes npm package security — published findings in 2023 showing that supply chain attacks on npm packages increased 633% year-over-year from 2020 to 2023. These attacks involve publishing malicious packages that mimic popular packages (typosquatting), injecting malware into legitimate packages through compromised maintainer accounts, or publishing packages that collect environment variables (which often contain API keys and secrets) and send them to attacker-controlled servers.
GitHub's 2023 State of the Octoverse report found that 84% of code in the average production application comes from open source packages rather than custom code written by the development team. This is the efficiency benefit of the package ecosystem made concrete — but it also means that 84% of your application's codebase was written by someone else, maintained by someone else, and secured by someone else's practices and incentives. The trust chain this creates is enormous and largely invisible.
The Types of Dependency Risk
Security risks are the most dramatic but not the only concern. Performance is a parallel dimension. The node_modules directory in a typical Next.js or React application can easily exceed 500MB. When dependencies are bundled for the browser, their code is sent to every visitor of your site. Tools like Bundlephobia (bundlephobia.com) allow you to analyze the bundle size impact of any npm package before installing it. The results are often surprising: moment.js (a date manipulation library that many projects default to) adds 67.9KB to a gzipped bundle. Its actively maintained successor date-fns adds 21KB and is modular — only what you use is bundled. The difference in user experience is measurable: Google's Core Web Vitals data shows that every 100KB increase in JavaScript bundle size increases Time to Interactive by approximately 1 second on a 4G mobile connection.
Maintenance risk is a third dimension. The OpenSSF (Open Source Security Foundation) published research in 2022 finding that 36% of npm packages with more than 1 million weekly downloads have only a single maintainer — meaning a package that thousands of projects depend on has one person responsible for security patches and updates. When that person loses interest, changes jobs, or has an accident, the package can go unmaintained indefinitely. Projects built on unmaintained packages accumulate security debt silently, until a CVE (Common Vulnerability and Exposure) is published and suddenly your production system has a known, public vulnerability.
"Every dependency you add is a vote of confidence in someone else's code, someone else's security practices, and someone else's continued interest in maintenance. Make that vote carefully."
The Principle: Native APIs Have Come a Long Way
Much of the npm ecosystem was built to fill gaps in browser and Node.js native APIs that existed in the early 2010s. Many of those gaps no longer exist. Before ES6 (2015), JavaScript had no native module system, no arrow functions, no template literals, no destructuring, no spread operator, no Promises, and no class syntax. The flood of utility packages that filled these gaps — lodash for utility functions, bluebird for promises, babel-polyfill for modern syntax in old browsers — made sense at the time.
The current state of the web platform is dramatically different. The Fetch API is natively available in all modern browsers and Node.js 18+, eliminating the need for axios in many cases. Array methods (map, filter, reduce, flat, flatMap, findIndex) handle most lodash use cases. The Intl API provides localization and date formatting without moment.js. CSS variables, grid, and flexbox eliminate the need for many CSS utility packages. The MDN Compatibility Data shows that most modern web APIs are available in browsers covering 95%+ of global web traffic. The question worth asking before installing any package: does a native API already do this?
Practical Guidelines: A Decision Framework for Dependencies
The Google Chrome team's 'Patterns for JavaScript' guide provides a useful principle: prefer small, focused packages over large, general-purpose ones; prefer packages with multiple active maintainers over single-maintainer packages; prefer packages with recent commits over packages with stale repositories. Additionally: check the package's npm download trend (npmtrends.com provides weekly download comparisons), check the package's GitHub issues for unresolved security reports, and check Snyk's vulnerability database to see if the package has known CVEs.
For every package you are considering, ask three questions before installing: (1) Can I implement this functionality in under 30 lines of custom code? If yes, do that instead. (2) Does a native API already solve this problem? Check MDN first. (3) Is this package actively maintained (commits in the last 6 months), and does it have multiple maintainers or an active organization behind it? If a package fails all three tests favorably — you cannot write it yourself quickly, no native API exists, and it is well-maintained — then it is worth adding.
The India Developer Context: Dependency Bloat and Mobile Performance
For Indian web developers building for Indian audiences, the performance dimension of dependency bloat is particularly significant. As of 2024, approximately 67% of India's internet users access the web primarily through mobile devices (TRAI data), the majority on mid-range Android devices with limited processing power and RAM. Statcounter data shows that 4G remains the primary connection type for over 80% of Indian mobile internet users, with download speeds averaging 15–25 Mbps — functional but not the 100+ Mbps of fiber broadband.
In this context, every unnecessary dependency has a disproportionate impact on user experience. A JavaScript bundle that loads in 1.5 seconds on a MacBook in Bengaluru loads in 4 seconds on a mid-range Android in Patna. The 15-30% performance improvement typically achievable through dependency audit and removal can be the difference between a product that feels native and one that feels frustratingly slow — and slow digital products in India are not just inconvenient, they are abandoned. Google's research on page speed found that mobile site bounce rates increase 32% as page load time goes from 1 to 3 seconds.
"The best code is no code. The second-best code is code you wrote and understand completely. A dependency is always a distant third — use it only when the alternative is genuinely worse." — Adapted from Rich Harris, Svelte creator