Compressing JavaScript
From source to wire: minification then gzip/brotli, and how much each step shaves off the transfer.
JavaScript is the second-biggest thing most pages download — so shrinking the bytes on the wire is one of the highest-leverage wins. It happens in two stages: minification rewrites your code smaller, then HTTP compression (gzip / brotli) squeezes it further for the trip to the browser. This lesson covers both — and the trade-offs that can quietly cancel them out.
Minification
Minification makes the code itself smaller while keeping it valid JavaScript the engine runs identically. It strips comments and whitespace, drops dead code, and — the big one — mangles identifiers, renaming your firstNumbers to a. Step through it:
HTTP compression
Minified code is still plain text full of repetition — perfect for a compression algorithm. Compression comes in two flavours: lossy (throws away detail you won't miss — great for JPEGs, ~50% smaller) and lossless (decompresses to a byte-perfect copy). Text must be lossless — a missing character would break your code — so HTML, CSS, and JS always use lossless algorithms like gzip and brotli.
gzip vs brotli — level vs speed
gzip is the ~30-year-old standard, supported literally everywhere. brotli (2015) builds on the same ideas (LZ77 duplicate-detection + Huffman coding) with a bigger window and a built-in dictionary, so it compresses tighter. Both expose a level: higher = smaller file but more CPU time, with diminishing returns at the top. Play:
Static vs dynamic compression
Static (build-time) compression runs once when you build, saving a pre-compressed file on the server. Since it's offline, you can afford the highest levels — CPU cost doesn't touch request time. Best for assets that don't change often (your JS bundles). Dynamic (on-the-fly) compression runs per request in the server / proxy (nginx, Apache). Easier to set up and great for frequently-changing content — but you must use a lower level, or the compression time itself slows the response you were trying to speed up.
| Static (build-time) | Dynamic (on-the-fly) | |
|---|---|---|
| When | Once, during build | Per request, at the server |
| Level you can afford | Highest (offline) | Lower (adds latency) |
| Best for | Stable bundles | Frequently-changing content |
| Done by | Bundler plugin | nginx / Apache / proxy |
The browser/server handshake
You never decompress anything by hand. The browser advertises what it supports, the server picks one and labels the response, and the browser decompresses transparently before your code runs:
To check it's actually working: a Lighthouse audit flags “enable text compression” with the potential savings, and the DevTools Network tab shows the Content-Encoding header on each response.
Trade-offs with splitting & caching
Here's where the techniques in this module collide. Compression works better on bigger files — more repeated patterns to exploit. So aggressive bundle splitting into many tiny chunks can actuallyhurt the compression ratio. But a giant merged bundle hurts caching: change one line and the whole file's cache is invalidated, while split chunks let unchanged ones stay cached.
- →Two stages: minification (smaller code) then HTTP compression (smaller transfer).
- →Minification strips comments/whitespace/dead code and mangles names — still valid JS.
- →Text uses lossless compression; brotli generally beats gzip at a given size.
- →Higher levels = smaller but slower; use high levels static (build), low levels dynamic (per-request).
- →Mind the three-way trade-off: splitting (caching) vs merging (compression).