All articles written by AI. Learn more about our AI journalism
All articles

JavaScript's Bloat Problem Is Worse Than You Think

Why your web app downloads millions of lines of unnecessary JavaScript—and who's responsible for the mess we're in.

Written by AI. Bob Reynolds

March 30, 2026

Share:
This article was crafted by Bob Reynolds, an AI editorial voice. Learn more about AI-written articles
JavaScript's Bloat Problem Is Worse Than You Think

Photo: Theo - t3․gg / YouTube

A package called shebang-regex gets downloaded 133 million times per week. It contains two lines of code: a regular expression and an export statement. Another package, cli-boxes, is just a JSON file defining box-drawing characters. It pulls 40 million downloads weekly. Then there's slash, which replaces backslashes with forward slashes in file paths—one line of actual logic, 96 million weekly downloads.

Welcome to the JavaScript dependency crisis, where the typical web application downloads forests of code that shouldn't exist.

James Garbutt, a maintainer deeply embedded in the Node.js ecosystem, recently published an analysis identifying what he calls the three pillars of JavaScript bloat. His post landed in a community already exhausted by bundle sizes but perhaps not fully aware of why the problem persists. The video walkthrough of his findings, presented by developer Theo Browne, adds visceral frustration to Garbutt's technical breakdown.

The numbers are worse than most developers realize.

The Legacy Tax

The first pillar of bloat comes from supporting ancient JavaScript engines. Somewhere in the world, applications still run on ES3—the JavaScript specification from 1999. These are Internet Explorer 6 environments, early Node versions, systems that predate Array.forEach and Object.keys.

There's actually a company, HeroDevs, that specializes in this work. They maintain forks of Node.js 0.8. They backport security fixes to abandoned codebases. When a package maintainer wants to support these environments, they can't use any modern JavaScript features. Everything needs polyfills. Everything needs fallbacks.

"For those unfortunate souls who are still running old engines, they need to reimplement everything themselves or be provided with polyfills," Garbutt writes. Fair enough. The problem is that these accommodations aren't isolated to the people who need them.

Consider the package is-string. It checks if a value is a string. Simple enough. But is-string depends on has-tostringtag, which depends on has-symbols. It also depends on call-bind, which depends on get-intrinsic, which has its own sprawling dependency tree. All of this to check if something is a string, because someone somewhere might be running JavaScript older than most college students.

There's more. Some packages protect against "global namespace mutation"—the theoretical scenario where someone redefines Array.map and breaks everything. Node.js itself guards against this with "primordials," keeping pristine copies of built-in objects. Some maintainers believe user-space packages should do the same. Hence math-intrinsics, a package that does nothing but re-export JavaScript's built-in math functions so they can't be tampered with.

Then there are "realms"—the technical term for execution contexts like iframes. If you create an array in an iframe, it's not the same Array constructor as the parent page. So instanceof Array fails. Packages that need to work across realms can't use normal type checking. They need special detection code.

All three of these concerns—legacy engines, namespace protection, cross-realm compatibility—affect a tiny minority of developers. "The tiny group of people who actually need this stuff should be the ones seeking out special packages for it," Garbutt argues. "Instead, it is reversed and we all pay the cost."

When Good Intentions Backfire

The flashpoint came when a maintainer took over the axe-core accessibility testing package. The new steward focused on backward compatibility, adding 60 dependencies to support ancient Node versions. One of those dependencies, deep-equal, added 50 sub-dependencies by itself. The package went from nearly zero dependencies to a sprawling tree.

SvelteKit users saw their dependency count nearly double overnight. People thought it was a supply chain attack. It wasn't—just someone optimizing for a use case that represented perhaps one percent of users while imposing costs on the other ninety-nine.

"It's kind of crazy that a minor version bump of something that you're already using can suddenly double the number of dependencies that you have in your codebase," Browne notes in the video. The Svelte team was, understandably, furious.

Atomic Absurdity

The second pillar of bloat comes from what Garbutt calls "atomic architecture"—breaking code into the smallest possible reusable pieces. The Unix philosophy taken to a ridiculous extreme.

The package arrify converts a value to an array. If it's already an array, return it. If not, wrap it. That's the entire package. One line of meaningful code. Thirty-two million weekly downloads.

The package path-key checks if the platform is Windows and returns the appropriate environment variable name for PATH. Four kilobytes of JavaScript. One hundred fifty-eight million weekly downloads.

This isn't malice. It's a philosophical position about code reuse that made sense in theory but created a dependency catastrophe in practice. When you need to include a JSON file of box-drawing characters as an npm package instead of just... including the JSON file, something has gone fundamentally wrong.

"By splitting code up to this atomic level, the theory is that we can then create higher level packages simply by joining the dots," Garbutt writes. "I get this. I am a big fan of the Unix philosophy myself. But Unix philosophy should not be at a line of code level."

Who Benefits?

The frustration in Browne's video is palpable, but it's worth asking: why does this persist? The answer involves competing incentives that never quite align.

Package maintainers want broad compatibility. HeroDevs employees need to support ancient systems—that's literally their job. Developers like reusable code. npm makes it trivially easy to publish and depend on packages. None of these are bad things individually.

But the ecosystem lacks a forcing function to push legacy concerns out of the default path. There's no cost to adding dependencies. Bundle size shows up in metrics, but it's unclear which packages are the problem. Developers inherit dependency trees they never chose.

"These layers of niche compatibility somehow made their way into the hot path of everyday packages," Garbutt observes. The result is that everyone downloads support for Internet Explorer 6 whether they need it or not.

Garbutt's E18E initiative aims to address this through community cleanup—identifying redundant packages, removing outdated dependencies, modernizing where possible. It's unglamorous work. It's also necessary if JavaScript is going to dig itself out.

The bloat didn't appear overnight. It accumulated package by package, dependency by dependency, well-intentioned decision by well-intentioned decision. There's no villain here—just an ecosystem that optimized for the wrong things and is now paying the price in megabytes.

Bob Reynolds is Senior Technology Correspondent for Buzzrag.

Watch the Original Video

Saving the web from Javascript bloat

Saving the web from Javascript bloat

Theo - t3․gg

33m 24s
Watch on YouTube

About This Source

Theo - t3․gg

Theo - t3․gg

Theo - t3.gg is a burgeoning YouTube channel that has quickly amassed a following of 492,000 subscribers since launching in October 2025. Headed by Theo, a passionate software developer and AI enthusiast, the channel explores the realms of artificial intelligence, TypeScript, and innovative software development methodologies. Notable for initiatives like T3 Chat and the T3 Stack, Theo has carved out a niche as a knowledgeable and engaging figure in the tech community.

Read full source profile

More Like This

Related Topics