Representative interview topic

Backend interview: Design an actionable 429 and Retry-After contract

BackendMedium
Offer.cc Editorial TeamPublished Updated

Question

A public API limits requests by tenant, account, and endpoint. Clients often retry immediately after 429, slowing recovery, and SDKs interpret Retry-After differently. Design the server response contract and client behavior: when to retry, how to calculate waiting time, how to avoid a retry storm, and which metrics prove it works.

Prompt and scope

This tests the protocol boundary of rate limiting, not merely a Redis counter. RFC 6585 defines 429 for too many requests in a period and allows Retry-After; RFC 9110 defines seconds and an HTTP date. Turn those semantics into behavior that clients can safely execute.

What the interviewer is evaluating

  • Limit keys, windows, quotas, and response fields that explain who is limited and when recovery is possible.
  • Consistent 429 body, Retry-After, request ID, and safe quota details.
  • Client parsing of seconds or dates, jitter, deadlines, and attempt budgets.
  • Retry boundaries for non-idempotent writes, asynchronous jobs, and network timeouts.
  • Metrics for limit hits, recovery time, retry amplification, and eventual success.

Recommended answer structure

Define limit dimensions and error classes. Show a 429 response and the Retry-After rule. The client uses the header, request type, and local budget to wait, abandon, or switch to asynchronous work; every retry uses capped exponential backoff with jitter. Close with gateway, application, SDK, and business-operation responsibilities and fault-injection tests.

Deep dive: from response to retry

Make the limit explainable

Key limits by tenant, credential, IP, endpoint, or a global resource. Return a stable error type, request ID, and safe reason. If several limiters fire, use the longest known wait without revealing another tenant’s quota.

Generate Retry-After correctly

A delay means at least how many seconds to wait; an HTTP date means the recovery time. Compute internal windows with a monotonic clock, round up, and cap the value. Clients still need a minimum wait and jitter because dates can be affected by clock skew or proxies.

Parse and back off on the client

Honor Retry-After first; if it is missing or invalid, use capped exponential backoff. Give each logical request a total deadline and attempt budget. Coordinate concurrent work through a shared queue or token budget so many tasks do not wake together.

Respect idempotency boundaries

GET, HEAD, and explicitly idempotent PUT or DELETE are usually retryable. POST requires an idempotency key and matching server semantics. If a response was lost after execution, query status or reuse the same key instead of creating a second side effect.

Observe recovery, not just errors

Record limit dimension, 429 count, Retry-After distribution, client wait, retry amplification, eventual success, and abandonment. Segment by SDK version and tenant to distinguish immediate re-hit from legitimate traffic growth.

Sample answer

“I would rate-limit by tenant, credential, and endpoint and return a stable error type, request ID, safe reason, and Retry-After. The server calculates a monotonic-window delay, rounds it up, and caps it. SDKs parse Retry-After first and otherwise use jittered exponential backoff; each logical request has a deadline and attempt limit. GET is retryable; POST needs an idempotency key or a status query. I would monitor 429s, wait distribution, retry amplification, eventual success, and abandonment, then run a synchronized multi-client test to verify smooth recovery.”

Common failure modes and fixes

  • Retrying every failure → Classify by status, method semantics, and error type.
  • Ignoring Retry-After formats → Support seconds and HTTP dates and reject invalid values safely.
  • Immediate retry per thread → Share a budget, queue, and jitter.
  • Assuming POST is idempotent → Use an idempotency key, status query, or an explicit no-retry result.
  • Watching only 429 count → Measure wait, amplification, eventual success, and abandonment.

Scoring rubric and self-check

Strong answers cover limit keys, 429 semantics, Retry-After generation and parsing, jittered backoff, deadlines, idempotency, concurrency coordination, safe quota details, component ownership, and validation metrics.

Ask: Does the client know when recovery is possible? What if clocks differ? What if the header is missing? Can retries duplicate effects? How do concurrent tasks spread out? What proves recovery improved?

Follow-ups and extensions

Should Retry-After use seconds or a date?

Both are valid HTTP forms. Seconds suit short windows and avoid clock differences; a date can express a known recovery point. Keep server output consistent while making SDK parsing compatible with both and handling expired values.

If both gateway and application limit, which wait is returned?

The client-visible wait should cover all known limits, normally the longest, with a request ID. Internal telemetry records each layer so a gateway rejection is not misattributed to the application.

Can a client retry without Retry-After?

Only when method and error semantics permit it, a deadline remains, and the local backoff budget is sufficient. Use capped jittered backoff; for non-retryable writes return a queryable state or explicit failure.

How do you test a retry storm?

Synchronize many clients to trigger 429, then inject invalid headers, clock skew, connection timeouts, and recovery jitter. Observe arrival curves, amplification, recovery time, and eventual success.

Public sources

Related questions