Skip to content
aipatterns.dev

Category

Resilience Patterns

Patterns for calls that can fail, hang or come back wrong: bound the wait, count the failures, space the repeats, and continue with a lesser answer.

What these patterns solve

A dependency that fails slowly is worse than one that fails loudly. A hung socket holds a worker, the held workers exhaust the pool, and a dead shipping API becomes a dead checkout. And once a call does fail, the caller has three honest options: try again because the failure looked transient, stop trying because the dependency is down, or continue without the answer. Without patterns the defaults are the worst ones: unbounded waits, immediate repeats, and no plan B.

These patterns convert slow failure into fast failure, bound every repeat, and decide what the caller serves instead.

Members

  • Timeout: give every remote call a deadline shorter than the caller's own budget, cancel the work when it expires, and surface the timeout as a distinct error.
  • Circuit Breaker: count consecutive failures, refuse to call a dependency that just failed repeatedly, and let a probe test recovery before resuming.
  • Retry: re-attempt only retryable, repeatable operations a bounded number of times, honouring the server's Retry-After, and surface the last error.
  • Exponential Backoff: the spacing policy for repeats - double the wait each attempt, cap it, and jitter so concurrent callers do not retry in lockstep.
  • Fallback: return a pre-decided lesser answer - stale cache, static default, partial result - so the caller's journey continues, marked as degraded.
  • Rate Limiting: give each client a refillable budget of calls; refuse with 429 and a Retry-After when it runs out.

How to choose

  • A remote call has no explicit deadline: Timeout. This is the baseline; nothing else here works without it.
  • The error is transient-looking (timeouts, 429, 503) and the operation can be repeated safely: Retry with Exponential Backoff spacing.
  • Calls keep failing in bursts and callers pile up waiting or retrying: Circuit Breaker. It needs timeouts to classify hangs as failures first.
  • The user journey must continue without the data: Fallback - after retries are exhausted, or immediately while a breaker is open.
  • One client's burst starves the others: Rate Limiting. Ration per client before the resource notices the load.
  • The operation cannot be repeated safely (payments, writes without an idempotency key): none of them. Make it repeatable first (Command plus a durable queue with deduplication), or fail and let a human or workflow decide.

Prefer no pattern when

  • The call is in-process and cannot hang. A function call that throws needs no deadline.
  • The platform already enforces deadlines or retries end to end (gRPC deadlines, Go context, HTTP clients with built-in retry). Configure those instead of wrapping a second layer - two retry layers multiply, not add.
  • The failure is permanent (validation, missing resource, business rejection). Fix the input; do not repeat it.
  • The caller has no latency budget for a second attempt. Fail fast instead.

See also

Original writing and examples. Topic coverage follows Microsoft Azure Architecture Center, which explains this topic with its own illustrations.