Performance budgets at scale
A budget nobody's build can fail is a dashboard: it records the drift instead of stopping it.
Predict first
Before you read: predict
A payments ledger's checkout bundle is 200 kB (brotli). CI fails any merge request that grows the bundle by more than 5% of its size on main at that moment, and a failing merge request does not merge. Over a quarter, 52 merge requests are opened one after another: 48 small ones of 1, 2 or 3 kB, in turn, and four large ones of 10, 11, 12 and 11 kB, opened after the 3rd, 7th, 11th and 15th small ones.
To the nearest kB, how big is the bundle at the end of the quarter?
The 30-second version
A performance budget limits a metric: a quantity, a milestone timing or a rule-based score. It holds only as an absolute limit that fails CI, with a named owner. For latency, start from the request's p95, subtract the network floor and headroom, give each hop its share, and still check the request.
A budget nobody's build can fail is a dashboard: it records the drift instead of stopping it.
How it works
Step 1 of 12
Analogy: A haulier's transport office
The planner draws up each driver's day against the legal limit on driving hours.
| In the transport office | In a system |
|---|---|
| the planning software refuses a run sheet over the day's driving limit | a limit that fails CI (step 2) |
| a wall chart of last month's hours changes nothing | a report with no limit, a dashboard (the headline) |
| "only ten minutes more" at each drop adds up over a week | drift under a relative check (steps 3–4) |
| the transport manager answers for the hours and records each extended day she allows, and why | an owner who decides exceptions (steps 6–7) |
| start from the day's limit, take off the ferry crossing nobody can shorten and a spare half hour for traffic, then give each leg its share | a top-down latency split (step 9) |
Breaks down when: the law sets a driver's limit and nobody inside the firm may raise it; a team sets its own budget and can change it.
Contrasting case: a courier on a bicycle with one drop and no limit to plan against: no budget to keep, so nothing to drift from.
In real terms: put the limit in the build and a name next to it, and split a request's latency from the top.
Examples, run for real
4.1 ExamHall EXH-4300: a check on a file the build no longer writes
ExamHall's exam route had a size-limit entry with a 200 kB limit that named dist/exam.js. Once the build put a content hash in each chunk's name, size-limit matched no file and failed every merge request, so the team let the job fail without blocking, and nobody checked the route.
4.1 before: entry naming dist/exam.js · exam route no file matched the path · exit 1
4.1 MR: path dist/exam-*.js · exam route 189.9 kB within 200.0 · exit 0
MR !4600 names the chunk by pattern, dist/exam-*.js, makes the job block merges again and names the route's owner (steps 2 and 6). On the same build, CI passes, and the first merge request that takes the route past 200 kB fails.
4.2 Start-exam's 500 ms p95, split top down
Start-exam's p95 budget is 500 ms: edge 50 + API 150 + DB 250 + headroom 50 (step 9). Each hop's time is drawn with its own budget as its p95.
4.2 hop p95s edge 50 · API 150 · DB 252 ms (sum 450, plus 50 headroom = 500) · request p95 342 ms
The request's own p95 is measured too and kept as a budget beside the hops' (step 12).
Try it
Answer first, then check. Nothing is saved.
Question 1 of 6
A ticketing site's event page grew from 150 kB to 190 kB (brotli) over a month. The build writes the page's chunk as dist/event-3b7c.js (190 kB) and the shared libraries as dist/vendor-9a1f.js (130 kB); the hash in each name changes with its content. Reviewers proposed four entries, with a limit of 160 kB, 180 kB, 300 kB or none.
With size-limit run on every merge request, which single entry in .size-limit.json would have let the page grow with CI still green?
Question 2 of 6
A video service's play endpoint pays a network floor of 30 ms, then calls three services in sequence, each with a 70 ms p95 budget. Each service takes 20–40 ms, except 1 call in 25, at random, which takes 300 ms more; the services are slow independently. The interviewer says: every service meets its own p95 budget.
About what is the endpoint's p95, over 100,000 requests?
Question 3 of 6
A payments ledger's transfer endpoint has a p95 budget of 300 ms. The network between client and edge costs a network floor of 40 ms that no service can cut, and the team keeps 30 ms of headroom. The endpoint calls three services in sequence: auth (30 ms) and fraud scoring (80 ms) already have their p95 shares, and the ledger write has none yet.
How many milliseconds of p95 budget are left for the ledger write?
Question 4 of 6
A chat app's main bundle is 180 kB (brotli), size-limit fails CI above 200 kB, and only the bundle's owner may change the limit. Next year brings 32 merge requests of 2–6 kB (seeded, whole kB), and six of them are roadmap features the product team has committed to. The team wants to ship all six roadmap features and end the year at 230 kB or less.
Which rule should the owner follow for a merge request that would cross the limit?
Question 5 of 6
The payments ledger team from the opening question changes its check: CI now fails any merge request that leaves the checkout bundle above 220 kB. An author whose merge request fails splits it into two halves and opens each in turn, and so on down to half a kB, below which the change is abandoned. Starting again from 200 kB, the same 52 merge requests are opened in the same order.
By how many kB, to the nearest kB, does the bundle grow over the quarter?
Question 6 of 6
A ticketing site's checkout calls three services in sequence. Each service's time is random with a smooth tail (exponential), and each has a p95 of 100 ms; there is no network floor, and the services are independent.
Over 100,000 requests, about what is checkout's p95?
Go deeper
Pro tips
- When every service meets its own p95 budget and the endpoint still misses its own, look for a rare stall that each hop's p95 hides before re-splitting the budget.
When: a latency budget split across hops in sequence, with each hop green on its dashboard (steps 11–12). Verify:
Status: RAN.tip two hops each meet a 30 ms p95 (29.7, 29.7 ms) while the request p95 is 234 ms: check the request's p95 end to end, not only each hop's
Source
quote-check.mjs finds every passage below verbatim in its saved text. Valid as of 2026-10-15.
- web.dev, Performance budgets 101: https://web.dev/articles/performance-budgets-101?hl=en (
webdev-budgets-101.txt). The row's first source. Quoted: "A performance budget is a set of limits imposed on metrics that affect site performance." and "Milestone timings mark events that happen during page load". Supports step 1. - Andrey Sitnik, size-limit README: https://raw.githubusercontent.com/ai/size-limit/main/README.md (
size-limit-readme.txt). The row's second source. Quoted: "It checks every commit on CI, calculates the real cost of your JS for end-users and throws an error if the cost exceeds the limit." - web.dev, Interaction to Next Paint (INP): https://web.dev/articles/inp?hl=en (
webdev-inp.txt). The row's third source. Quoted: "An INP below or at 200 milliseconds means a page has good responsiveness" and "a good threshold to measure is the 75th percentile of page loads recorded in the field, segmented across mobile and desktop devices". Supports step 8.
Related lessons
Checked against the tools and docs as of 2026-10-15.