step 20 / 32 · 5 min
Making something faster, on purpose
Performance work done by intuition mostly optimises code that was not slow. Done with measurement it is usually a small change with a large effect.
Measure first
1. is it actually slow, and by whose standard
2. where does the time go
3. change one thing
4. measure again
5. stop when it is fast enoughStep 5 matters. Performance work has diminishing returns and a real cost in complexity, and "fast enough" is a decision, not a failure.
Percentiles, not averages
average 120ms looks fine
p50 80ms
p95 400ms
p99 3,200ms one in a hundred requests takes 3 secondsThe average hides the tail. On a page making 20 requests, a p99 of 3 seconds means most page loads contain one, so the experience most users get is the p99, not the median.
Report p50, p95 and p99. An average alone is close to useless.
Where the time usually is
| Cause | Typical |
|---|---|
| N plus one queries | one page, hundreds of queries |
| A missing index | one query, seconds |
| Waiting on a third party | the whole request |
| Doing work nobody asked for | eager loading everything |
| Serialising a large response | CPU, at the end |
| Too many round trips | latency times count |
Almost none of these are fixed by faster code. They are fixed by doing less.
Amdahl's law, informally
If a step is 20 percent of the total, making it instant improves the total by 20 percent and no more. Optimising the wrong step is bounded by how much of the time it was.
Which is why profiling before optimising is not pedantry: it tells you the ceiling on the change you are about to make.
The order to try
Do less. Fewer queries, less data, fewer round trips, no work nobody needs. Almost always the biggest win.
Do it earlier. Cache, precompute, materialise.
Do it later. Queue what the user does not wait for.
Do it in parallel. Independent calls at the same time rather than in sequence.
Do it faster. Better algorithms, then better code. Last, because it is the most effort for the least reward in most systems.
Watch it in production
Local benchmarks use small data, warm caches and no network. Production has real data volumes, cold caches, contention and latency. Improvements that hold locally and vanish in production are common enough to expect.