A failed response does not prove a failed operation
Imagine a client creating a delivery booking. The server stores the booking, but the connection closes before the response reaches the client. From the client’s perspective the request failed. From the server’s perspective it succeeded. Sending the same create request again can produce a second booking.
Retry design begins by separating two decisions: whether the failure might be temporary, and whether repeating the operation is safe. A temporary error alone answers only the first question.
Client Booking service
|---- create booking -------->|
| | stores booking
|<--- response lost ----------|
|---- repeat create? -------->| possible duplicateDefine the repetition contract
HTTP defines an idempotent method by the intended effect of repeating an identical request. RFC 9110 also cautions against automatically retrying a non-idempotent request unless the client knows it is safe or knows the original request was not applied. This is a property of the operation’s semantics, not a promise that every attempt returns an identical response.
For a create operation, an application can define its own idempotency contract. In our booking example, the client chooses a key for one logical booking, reuses it for retries, and the server associates it with the original request and result. Reusing a key for a different booking should be a conflict, not permission to return an unrelated result. Merely attaching a header does not create this guarantee; the server has to implement it.
Budget time for the whole request
Consider a caller willing to wait 900 milliseconds. An initial attempt takes 350 ms, a delay takes 200 ms, and 350 ms remain. Giving the next attempt a fresh 900 ms timeout breaks the caller’s original budget even though each individual timeout is bounded.
Track an overall deadline and account for both work and waiting. Decide how cancellation behaves during an attempt and during a delay. Backoff spreads attempts over time, but it cannot make an unsafe write safe or authorize work after the caller has given up. An attempt limit and a time budget serve different purposes; a request can exhaust either first.
- What evidence allows this operation to be repeated?
- Which failures are candidates for retry under this service’s contract?
- How much of the caller’s budget remains before the next attempt?
- What stops an in-flight operation or waiting period after cancellation?
Test transitions, side effects, and timing
Use a controlled transport and clock to describe a sequence of outcomes without waiting in real time. Record each attempted operation, its logical identity, and the time consumed. Verify the eventual result and the number of side effects. A test that checks only the final status can miss a duplicate booking.
For the worked example, test response loss after a committed booking, a repeated key with the same payload, a repeated key with a different payload, and exhaustion during backoff. These are general design probes, not a complete production retry policy. Provider rules, storage atomicity, retention windows, and cancellation support still need their own specifications.
Review the policy and its caller together
An assistant may correctly identify retryable responses while overlooking the caller’s write semantics. Trace where request identity comes from, where the deadline is established, and which layer performs retries. Nested retry loops can multiply attempts even when each local loop looks modest.
In an interview, state your assumptions before editing. Explain why the original failure is reproducible, what the patch changes, and how the verification observes duplicate work. Practicing the HTTP retry client and payment webhook exercises gives you two different places to apply the same reasoning: outgoing requests and repeated incoming events.