All articles
December 27, 2025 7 min read

Designing for scale: caching, async work, and latency budgets

Scaling a multi-hop AI pipeline is less about raw horsepower and more about caching what's repeated, doing slow work asynchronously, budgeting tail latency, and degrading gracefully when a dependency fails.

Written forEngineering
Distributed SystemsScaleInfrastructure

A production AI system is a chain of hops — retrieve, rerank, call a model, call a tool — and each hop adds latency and a chance to fail. Designing for scale is mostly about four disciplines: cache what repeats, move slow work off the request path, budget latency across the chain, and fail without falling over.

Caching at every layer

The cheapest request is the one you don't compute. A cache like Redis in front of expensive lookups, a CDN for static and cacheable responses, and read replicas to spread database load all cut both latency and cost. In AI systems, semantic caching (serve a cached answer for a semantically similar query) and prompt caching (reuse the stable prompt prefix — see that post) are the model-layer equivalents. The art is invalidation: a stale cache is worse than none when the data changed underneath it.

Do slow work asynchronously

Anything that doesn't have to finish before you respond shouldn't. Push it to a job queue and process it with a pool of workers, with concurrency control so you don't overwhelm a downstream dependency. The request returns fast; the heavy lifting happens behind it. This is the same decoupling as the data-pipeline post, applied to the request path.

Budget latency in percentiles, not averages

Averages lie. What users feel is the tail — p95 and p99 — and in a multi-hop pipeline the tails compound: if each of five hops has a p99 of 200ms, the end-to-end p99 is far worse than 200ms, because the slow hops rarely line up but always add. Budget a latency allowance per hop, measure p50/p95/p99 at each, and attack the hop that blows its budget. Tail latency, not the mean, is the number to design against.

Scale out, and fail gracefully

Horizontal scaling (more stateless instances behind a load balancer, with autoscaling on load) beats vertical scaling for resilience and cost. And design for partial failure: a circuit breaker stops hammering a dependency that's down (and gives it room to recover), while graceful degradation returns a reduced-but-useful result — cached data, a cheaper model, a 'try again' — instead of a hard error. A system that degrades is one that survives its dependencies' bad days.

Scale isn't a bigger machine — it's caching the repeated, deferring the slow, budgeting the tail, and degrading instead of dying when something downstream inevitably breaks.
Building something with LLMs?
I help teams ship GenAI that’s reliable and cost-efficient.
Let’s talk