Skip to content
AB

Why Automatic Retries Are Dangerous for Swift API Clients

Retry transient reads with bounded jitter while protecting writes from duplicate business effects.

Ashish BhakhandPublished

A timeout does not prove the server did nothing. It may have completed a POST before the response disappeared. Retrying blindly can create two purchases, messages, or workout sessions.

I make retry behavior opt in:

Code
enum RetryPolicy {
    case none
    case idempotent
}

The client applies the idempotent policy to safe reads by default and rejects accidental use on POST, PATCH, or DELETE unless the caller explicitly supplies a server-supported idempotency design.

Retry connection failures and selected temporary gateway statuses. Do not retry validation, authorization, decoding, or ordinary conflicts.

Use a small attempt limit with exponential backoff and jitter:

Code
500 ms ± jitter
1 s ± jitter
stop

Respect Retry-After for rate limits when it is reasonable. Cancellation should stop sleeping and end the operation immediately.

For writes that are safe to replay, send a stable idempotency key and reuse it across attempts. A newly generated key on every retry defeats the guarantee.

Test the policy with a controlled URL protocol: temporary failure followed by success, permanent failure, cancellation during backoff, and a write that must remain single-attempt. Reliability comes from knowing which operations may repeat, not from retrying everything that failed.

← Back to all writing