Quick answer
A retry repeats an operation after a failure. Retries are useful for transient failures such as timeouts, connection resets, rate limits, and temporary dependency errors.
Unsafe retries can make outages worse or duplicate side effects. Good retry design uses timeouts, backoff, jitter, retry limits, idempotency, and clear rules about which failures are retryable.
Why retries exist
Distributed systems fail in temporary ways:
- A network packet is dropped.
- A database connection is reset.
- A dependency returns
503 Service Unavailable. - A queue consumer crashes before acknowledgment.
- A rate limit asks the client to wait.
Retrying can turn a temporary failure into a successful operation. But retrying everything immediately creates pressure exactly when the system is already unhealthy.
Timeout first
A retry policy without a timeout is incomplete.
If a request can hang for 60 seconds and then retry three times, one logical operation can tie up resources for minutes. Set realistic timeouts for each dependency before deciding how many retries are allowed.
Timeouts should reflect the operation. A cache lookup might have a very short timeout. A report export may have a longer async workflow instead of a long synchronous request.
Exponential backoff
Exponential backoff increases the delay between attempts.
attempt 1: now
attempt 2: wait 100 ms
attempt 3: wait 200 ms
attempt 4: wait 400 ms
attempt 5: wait 800 ms
Backoff gives the dependency time to recover and reduces retry pressure.
Cap the maximum delay so retries do not grow without bound.
Jitter
Jitter adds randomness to retry delays.
Without jitter, many clients can retry at the same time:
10,000 clients fail -> all wait 1 second -> all retry together
That creates a thundering herd. Jitter spreads retries across time.
wait between 500 ms and 1500 ms
Jitter is especially important for public APIs, mobile clients, scheduled jobs, and distributed workers.
Retry budgets
A retry budget limits how much extra traffic retries can create.
For example, a service may allow retries to add at most 10 percent more requests than original traffic. If the system is already failing heavily, the budget stops retries from multiplying the problem.
This is an advanced but useful idea for large systems because retries are not free. Every retry consumes capacity.
What to retry
Usually retryable:
- Connection reset.
- Timeout where the operation is safe to repeat.
429 Too Many Requestsafter respectingRetry-After.503 Service Unavailable.- Queue message processing after transient dependency failure.
Usually not retryable:
- Validation errors.
- Authentication failures.
- Authorization failures.
- Missing resources.
- Business rule conflicts.
Use REST API Status Codes Explained and the HTTP Status Code Lookup when designing response-specific retry behavior.
Idempotency
Retries are safest when the operation is idempotent.
For read requests, retrying is usually safe. For writes, a retry can create duplicate orders, duplicate emails, duplicate charges, or duplicate jobs unless the backend protects the operation.
Use idempotency keys for side-effecting API operations that clients may retry. Read Idempotency in APIs Explained for the full pattern.
Retries and circuit breakers
Retries try again. Circuit breakers stop trying temporarily when a dependency appears unhealthy.
These patterns work well together:
- Timeout prevents hanging calls.
- Retry handles small transient failures.
- Backoff and jitter reduce retry bursts.
- Circuit breaker prevents repeated calls to a failing dependency.
Read Circuit Breaker Pattern Explained for the fail-fast side of resilience design.
Layered retry design
Retries often exist in more than one place: browser client, SDK, API gateway, service client, queue worker, and database driver. If each layer retries independently, one user action can multiply into dozens of attempts.
A safer design assigns responsibility:
- Client retries only safe network failures or
429responses withRetry-After. - Gateway retries only idempotent upstream requests and uses a small attempt count.
- Services retry dependency calls with tight timeouts and jitter.
- Workers retry async jobs with longer delays and dead-letter handling.
For example, an API that creates an invoice should not be retried blindly at every layer. The client can send an idempotency key, the API can store the request outcome, and the worker can retry the provider call with the same key. That makes retries useful without creating duplicate invoices.
Choosing retry limits
Retry limits should reflect user experience and system capacity. A synchronous API request may only have room for one fast retry before returning an error. A background job can retry over minutes or hours because it is outside the request path.
Use metrics to tune limits rather than guessing. Track attempts per operation, success after retry, retry exhaustion, dependency error rates, and added traffic from retries. If most retries never succeed, the policy is adding load without improving reliability. If many operations succeed on the first retry after a short timeout, a small retry budget may be valuable.
Common mistakes
The first mistake is retrying immediately in a tight loop. That can overload the dependency and the caller.
The second mistake is retrying non-idempotent writes without protection.
The third mistake is retrying every status code. A 400 validation error will not become valid by trying again.
The fourth mistake is stacking retries across layers. A client retries three times, the gateway retries three times, and the service retries three times. One user action can become many backend calls.
Related reading
Read Idempotency in APIs Explained for safe write retries and Rate Limiting in APIs Explained for 429 behavior. For dependency failure protection, read Circuit Breaker Pattern Explained. For queue-specific retries, read Message Queues Explained.