The p99 is a lie you tell yourself
You cannot add percentiles. Every latency budget that does is a budget nobody is actually keeping.
Vedant · 1 note in the margin · reviewed 2mo ago
You cannot add percentiles. Every latency budget that does is a budget nobody is actually keeping.
Vedant · 1 note in the margin · reviewed 2mo ago
Viewing revision 1 — the first published version. Written 8 months ago.
← Back to the current versionThere is a specific kind of incident where every dashboard is green and the customers are still complaining, and the cause is almost always this: someone averaged a percentile.
A p99 is the value that 99% of requests came in under. It is a property of a distribution, not a quantity you can decompose. If service A has a p99 of 40ms and service B has a p99 of 60ms, you have learned essentially nothing about the p99 of the pair, because the 1% that is slow in A and the 1% that is slow in B are very unlikely to be the same 1% of requests.
very unlikely to be the same 1%
I push back slightly on "very unlikely". If both services degrade from a shared cause — a noisy neighbour, a bad deploy that touched both, a GC pause — the slow tails are *highly* correlated. In that regime naive addition is close to right, which is exactly why the mistake survives: it is right often enough to feel validated.
The inequality is the interesting part. p99(A + B) ≤ p99(A) + p99(B) holds always, so summing can only ever overstate — the direction is not in doubt. What is in doubt is the size of the error, and that depends entirely on how correlated the tails are. If your hops fail together, the sum is exactly right. If they fail independently, a real fraction of the budget you just committed to is imaginary. Nothing in either dashboard tells you which world you are in.
The naive sum is 191ms. The real number is somewhere between 159ms and 192ms, and which end you get depends on whether your hops fail together. You cannot read that off the component dashboards.
20,000 simulated requests, lognormal per hop. The honest answer depends entirely on how correlated the hops are.
Worth being precise about why 4% is a floor rather than an estimate. Real hops are correlated by traffic shape — a cache stampede hits the edge, the service and the database simultaneously — which pushes the true figure higher. You will not see a spike where all four are slow at once very often, but when you do, it is catastrophic rather than merely bad.
Measure the percentile on the whole request, at the edge, with the trace id attached. That is the only p99 that means anything. Everything else is a component-level diagnostic wearing a system-level label.
The uncomfortable part: this requires trace propagation to be correct, which is a larger project than the SLO was. That is the real cost of a meaningful latency budget, and it is the cost people avoid by averaging. I have now watched three teams take the averaging route and every one of them ended up with an SLO that improved exactly as much as the service got worse.
A caveat on the 4% figure, which readers have correctly pushed back on twice. It assumes the slow tails of the four hops are independent. Under a shared cause — a noisy neighbour, a deploy that touched every service, a GC pause — the tails are highly correlated and naive addition is closer to right. That is precisely why the mistake survives: it is right often enough to feel validated, and the incidents where it is badly wrong are the ones you do not get a page for.
First time here? Set your depth once and every post honors it — skim the argument, read it properly, or take the footnotes too.
Full argument, prerequisites explained inline.
1 note from 1 reader, attached to specific sentences. Select any sentence in the text to add yours.
Answers are quotes from this post, not generated text — so they can be wrong by omission but never by invention. What it does not cover, it says so. If you have brought your own model key, a model summarizes those same quotes, and the quotes stay the source of truth.
Your key is used for your provider if you added one, otherwise the site's. Automatic stays on free-tier models only.
The claims this post makes most concretely — the ones with numbers, absolutes or prescriptions in them — put to a model that has read only the sentence in question. Every objection quotes the post, and a claim nothing can be said against is reported as standing.
It leans on 2 other posts. Nothing has picked it up yet. Oldest at the top, so a claim resting on something written after it would be visible as an arc running upward.
Leave an address and you will hear if this post is ever substantially revised — once, and only when it actually changes.