skip to content

A frontend team writes its JavaScript bundle budget as "150 KB". Which measurement should that number refer to — the file on disk or the bytes on the wire — and why does the budget also have to name the compression algorithm?

level: juniorimportance: should knowfreq 55%

answer

  1. the wire, not the disk
  2. two algorithms, two numbers
  3. quality level changes the result
  4. state the unit and the encoding

basics

~20 s

A transfer budget should be measured on the compressed bytes actually sent, not the file on disk, and it must name the algorithm: brotli output for the same JavaScript is typically noticeably smaller than gzip, so "150 KB" is ambiguous otherwise.

solid answer

~50 s

A bundle budget is a proxy for what the user pays to download, so it should be measured on the **compressed** bytes that leave the server, not on the minified file sitting in `dist/`. Those are very different numbers — minified JavaScript commonly compresses three to four times over, so a 480 KB file can be ~120 KB on the wire. Because gzip and brotli produce different sizes for the same input (brotli is usually the smaller of the two for text), and because compression quality levels also change the result, a budget that just says "150 KB" is unfalsifiable. Write it as "150 KB brotli, quality 11" or "150 KB gzip -9", measure it the same way in CI every time, and confirm separately that production actually serves that encoding — a budget measured with brotli against a server that only sends gzip is measuring a file nobody downloads.

code

bash · 3 lines
bash
wc -c < dist/main.js
gzip -9 -c dist/main.js | wc -c
brotli -q 11 -c dist/main.js | wc -c

go deeper

for a junior

Know that a bundle has a raw size and a smaller compressed size, and that a network budget refers to the compressed one. Be able to say plainly that gzip and brotli give different numbers.

for a middle

Explain the pipeline: minify, then compress, then the browser decompresses before parsing. Be ready to say roughly how much minified JavaScript compresses and why brotli usually beats gzip on text.

for a senior

Show that you verify the budget against what production actually serves — the response encoding and transferred size — and that you know build-time brotli 11 flatters a setup where the edge compresses on the fly at a lower quality.

for a principal

Own the definition itself: one measurement method, one unit convention, one place the number lives, applied identically in CI and in reports, so size claims across teams are comparable rather than each team quoting its favourite tool's output.

## Three different sizes of the same bundle Every built JavaScript asset has at least three sizes, and a budget is meaningless until you say which one it constrains. 1. **Raw / on-disk size** — the minified file the bundler wrote. This is the number a file listing shows and the number most bundlers print in their build summary. 2. **Transfer size** — the bytes that actually cross the network after the server applies `Content-Encoding` (gzip, brotli, or zstd where supported). For text assets this is typically a third to a quarter of the raw size. 3. **Decompressed size in the browser** — the browser inflates the response back to the raw bytes before parsing it. This one equals the raw size and is what the JavaScript engine has to parse and compile. A *transfer* budget is about the network: how many bytes a user on a slow connection must pull before the page can work. That is the compressed number. ## Why the algorithm has to be named Compression is not one thing. Gzip (DEFLATE) and brotli are different algorithms with different results on the same input, and brotli additionally ships a built-in dictionary tuned for web text, so it usually wins on HTML/CSS/JS. For a typical bundle, brotli output lands meaningfully below gzip output — enough that a bundle can be inside budget under one and outside it under the other. Quality level matters too. Brotli exposes levels 0–11; level 11 is slow and is what you use when compressing **once** at build time, while a CDN or origin doing **on-the-fly** compression for every response usually runs a much lower level to keep CPU down. So a local `brotli -q 11` measurement can be optimistic relative to what production really sends. Either pre-compress at build time and ship the `.br` files, or measure at the quality your edge actually uses. ```bash # same file, three numbers wc -c < dist/main.js # raw, minified gzip -9 -c dist/main.js | wc -c # gzip transfer size brotli -q 11 -c dist/main.js | wc -c ``` ## Minification is a separate step A common muddle is treating minification and compression as the same thing. Minification is a *source transform*: renaming locals, dropping whitespace and comments, folding constants. It happens once, in the build, and its output is still valid JavaScript. Compression is a *byte encoding* applied to the response and undone by the browser before parsing. They compose — minify first, then compress — and minification is still worth doing even though compressed sizes converge somewhat, because the decompressed bytes are what gets parsed. ## Writing a budget that can actually fail A usable budget statement pins down four things: - **The artifact**: which file or which group of files (an entry point, a route's initial payload, "all JS", "all fonts"). - **The measurement**: compressed transfer size, and with which algorithm and quality. - **The number and its unit**: be explicit about KB (1000 bytes) versus KiB (1024) — tools differ, and the gap is about 2.4%, which is inside the range of a real regression. - **The consequence**: warn, or fail the build. Then measure the same way every time. The most common way a budget quietly stops working is that the number in the wiki was measured with one tool and the check in CI uses another, so the two drift and nobody trusts either. ## Verifying the number is real The last step people skip: check what production serves. Load the asset and look at the response's `Content-Encoding` and the transferred size the network panel reports. If the origin or CDN is not compressing that asset — a common miss for files served from an object store with the wrong content type, or for already-`.gz`-named files — your careful brotli budget describes a file that no user ever receives. The budget is a claim about user experience, so it should be anchored to something you can observe on the live site, not only to a number computed in the build.

  • Your CI budget check passes with brotli quality 11, but real users seem to download more than that. What would you check first?
    Whether production actually serves brotli for that asset, and at what quality. Look at the response's `Content-Encoding` and the reported transfer size on the live site. On-the-fly compression at the edge runs a much lower quality than 11, and a misconfigured origin may serve the asset uncompressed entirely — in which case the user pays the raw size.
  • If compression already shrinks JavaScript so much, is minification still worth the build time?
    Yes. Compression is undone before parsing, so the browser still parses every minified-away byte otherwise. Minification cuts the decompressed bytes the engine must tokenise and compile, and it also enables dead-code removal that compression cannot do, since compression only re-encodes bytes and never understands the program.

Quoting a bundle's on-disk size is like quoting a suitcase's weight before you vacuum-pack it: the airline charges for what actually arrives at the counter.

saying these in an interview costs you the question

  • Quotes the on-disk size as the bundle's cost to users
  • Assumes gzip and brotli give the same number
  • Treats minification and compression as one step
  • Measures locally at brotli 11 while the CDN compresses on the fly
  • Mixes KB and KiB when comparing against the budget

context