Vivian Voss

The Webpack Tax

javascript tooling web

Performance-Fresser ■ Episode 13

In 2012, JavaScript had a module problem. The browser did not understand require(). Node.js used CommonJS. AMD existed for those with a particular appetite for acronyms. And somewhere in between, a developer named Tobias Koppers built a tool that could take all these competing module formats, resolve their dependency graphs, and emit a single file the browser could execute. It was called Webpack, and it was, at the time, genuinely useful.

In 2017, ES Modules shipped natively in every major browser. The problem Webpack was built to solve (that browsers could not understand module imports) ceased to exist. import and export became part of the language. The browser understood them. The build step became optional.

It is now 2026. Webpack is nine years older than the problem it solves. And yet 86% of JavaScript developers still use it. The bundler that bundles your patience.

The Build That Time Forgot

Evan Wallace released esbuild in 2020, written in Go, and published benchmarks that read less like a comparison and more like an autopsy report. Three.js, a substantial, real-world codebase, bundled in 0.39 seconds by esbuild. Webpack 5 required 41.21 seconds. That is not a percentage improvement. That is a 106x difference.

The TypeScript compiler for the Rome project: esbuild managed 0.10 seconds. Webpack 5 needed 16.69 seconds. A factor of 167. One hundred and sixty-seven times slower, to produce the same output, from the same input, on the same machine.

BUILD TIME: SAME INPUT, SAME OUTPUT Three.js esbuild 0.39 s Webpack 5 41.21 s 106x slower TypeScript (Rome) esbuild 0.10 s Webpack 5 16.69 s 167x slower Source: esbuild.github.io Both tools: same input files, same output format, same machine

One might argue that benchmarks are synthetic, that real-world projects differ, that context matters. One would be correct about all three. And yet, when the magnitude is a hundred-fold, context becomes a footnote. Webpack is not slightly slower. It is categorically slower, in the way that a horse-drawn carriage is categorically slower than an aeroplane. Both reach the destination. One of them allows you to have lunch first.

The Memory Hole

Build time is the visible cost. Memory consumption is the one nobody watches until the CI server falls over.

Webpack 4, on a mid-sized production project, consumed between 500 and 800 MB of RAM. Unpleasant, but manageable. Webpack 5, the version marketed as the performance upgrade, consumed 3.8 GB on the same project. Not a different project. Not a larger codebase. The same files, the same output, and nearly five times the memory.

The root cause is terser-webpack-plugin, Webpack’s default minifier. It spawns worker threads for parallel minification, a sensible optimisation in theory. In practice, each worker clones the entire AST into its own memory space. Four workers, four copies of the abstract syntax tree, four times the allocation, and a garbage collector that cannot reclaim any of it until all workers complete. The parallelism that was supposed to save time costs memory instead, and the trade-off was made without asking.

GitLab’s development environment, powered by Webpack, has been documented at 30 GB of RAM. Thirty gigabytes. For a development environment. For a tool whose job is to concatenate files.

The Developer Experience Tax

A developer’s day begins with a cold start. Open the project, start the dev server, wait. How long one waits depends, rather dramatically, on the bundler.

Vite, built on esbuild for dependency pre-bundling and native ES Modules for serving, cold-starts a project with a thousand React components in 1.7 seconds. Webpack 5, on the same project, requires 5.6 seconds. Three times slower to reach the point where development can begin.

But cold starts happen once. Hot Module Replacement happens hundreds of times a day, every save, every change, every iteration. This is where the tax becomes a salary deduction.

DEVELOPER EXPERIENCE: 1,000 REACT COMPONENTS Cold Start Vite 1.7 s Webpack 5 5.6 s 3.3x slower Hot Module Replacement Vite 10-20 ms Webpack 5 500-1600 ms 50-80x slower 200 saves/day × 1 second lost per save 3 minutes 20 seconds per developer, per day, staring at a spinner Source: github.com/farm-fe/performance-compare

Vite updates the browser in 10 to 20 milliseconds. Webpack takes 500 to 1,600 milliseconds. On every single save. Two hundred saves a day, a conservative estimate for an active developer, and Webpack costs three minutes and twenty seconds of dead air. Not thinking time. Not debugging. Staring at a browser that has not yet caught up with the code you wrote two seconds ago.

Multiply by a team of twenty. Multiply by two hundred and fifty working days. The figure stops being an inconvenience and starts appearing on a spreadsheet.

The Configuration Cathedral

esbuild’s configuration for a production build fits in a function call. Webpack’s configuration is a Gothic cathedral of loaders, plugins, rules, resolve aliases, optimisation strategies, and split-chunk heuristics. Thirty-plus configuration categories. Two hundred lines before the first line of application code is processed.

The Webpack documentation, to its credit, is comprehensive. It has to be. The surface area of what can go wrong is so vast that the documentation is less a guide and more a survival manual. One does not learn Webpack configuration. One inherits it, from a colleague who has since left the company, in a file that has not been touched in fourteen months because the last person who touched it spent two days fixing the cascade of errors that followed.

The most common Webpack configuration strategy: copy it from Stack Overflow and pray.

The configuration file becomes load-bearing archaeology. Every module.rules entry has a story. Every resolve.alias was a workaround for something. Every plugin was added to fix a problem caused by another plugin. The configuration does not describe a build process. It describes the accumulated anxieties of every developer who has touched the project.

The Sentiment Index

Numbers measure performance. Surveys measure pain.

The State of JavaScript 2024 survey paints a portrait of a tool that the industry uses out of inertia rather than enthusiasm. Webpack’s usage stands at 86%, an enormous installed base. Its satisfaction score: 14% positive. Its dislike ratio: 37%. More than a third of its users actively dislike it. Only 14% would recommend it.

Vite, by contrast, reports a 98% retention rate. Ninety-eight per cent of developers who try Vite continue to use it. One does not typically see retention figures that high outside of subscription services that make cancellation deliberately difficult.

STATE OF JS 2024: BUILD TOOLS WEBPACK Usage 86% Dislike 37% Positive 14% vs VITE Retention 98% 86% use Webpack. 14% are happy about it. Source: 2024.stateofjs.com

The gap between Webpack’s usage and its satisfaction is the gap between habit and choice. Eighty-six per cent of developers use Webpack because their project already uses Webpack, because the CI pipeline expects Webpack, because migrating away from Webpack requires touching the configuration file, and nobody wants to touch the configuration file. Fourteen per cent would choose it again. The rest are hostages.

The Problem That Solved Itself

Webpack was built to solve a genuine problem: browsers could not understand JavaScript modules. In 2012, if you wanted to split your code across multiple files, you needed a tool to reassemble them into something the browser could execute. Webpack did this. It did it well. It earned its place.

Then the platform caught up.

ES Modules shipped in Chrome 61 (September 2017), Firefox 60 (May 2018), Safari 11 (September 2017), and Edge 16 (October 2017). The <script type="module"> attribute told the browser to treat the file as a module. import and export worked natively. No bundler. No loader. No configuration file. The browser understood modules because the specification said it should, and the browser vendors implemented the specification.

That was nine years ago. The problem Webpack solves has been solved, by the platform, natively, for nine years. And yet Webpack persists, not because the problem returned, but because the ecosystem built on top of the solution never noticed the problem had left.

The Invoice

Let us itemise.

106x slower than esbuild on Three.js. 167x slower on TypeScript compilation. 3.8 GB of RAM for a project that Webpack 4 handled in 800 MB. 30 GB in GitLab’s dev environment. Cold starts 3.3x slower than Vite. HMR 50 to 80 times slower. A configuration system with thirty-plus categories that no single developer fully understands. An 86% usage rate with a 14% satisfaction score.

Every one of these costs is documented. Every one is measurable. Every one is paid daily: in developer time waiting for builds, in CI minutes burning compute, in RAM allocated to a tool that concatenates files with the resource appetite of a database server.

Webpack was the right tool for 2012. It is the inherited tool of 2026. The distinction between those two is the Webpack Tax: the cost of continuing to pay for a problem the platform solved nine years ago, because the configuration file is too frightening to replace.