Platform Engineering27 October 20266 min read

Architecture in Plain English — Part 5 of 8

HTTP status codes as operational signals

A status code is an instruction to the caller: fix your request, slow down, try again later, or do not try again at all. Part 5 explains which responses are retryable, how to handle rate limits, and why every failure needs a correlation ID.

#Platform Engineering#Solution Architecture#Production Readiness#Observability

Most explanations of HTTP status codes are cheat sheets: 200 means OK, 404 means not found, 500 means server error. They are accurate and not very useful, because the person who needs status codes most is not reading a cheat sheet. It is the code on the other end of the call, deciding at three in the morning whether to try again.

This part treats status codes the way an operations team should: as instructions about what the caller should do next.

Marula Commerce is fictional, and its systems and figures are illustrative. Marula's order service calls its own catalogue service, an external payments provider, the warehouse system and a courier API. Its partners call Marula through an API gateway. Each of those calls returns a status code, and each code should change what happens next.

The families, read as instructions

Status codes come in families defined by their first digit. The current standard for their meaning is RFC 9110, HTTP Semantics, published in 2022, with 429 defined separately in RFC 6585.

  • 2xx: it worked. The request was received and handled. A 202 Accepted means "received, not finished": the caller must check later for the outcome.
  • 3xx: look elsewhere. The resource has moved or the cached copy is still valid. Most API clients follow these automatically.
  • 4xx: the problem is the request. Something about who is calling, what they asked for or how they asked is wrong. Sending the same request again will produce the same answer.
  • 5xx: the problem is on the server side. The request may have been fine. Something upstream failed, timed out or is temporarily unavailable.

The single most important rule follows from this: retrying a 4xx response is almost always wrong, and retrying a 5xx response is only safe if the operation is idempotent.

Status codes as instructions to the callerCODEWHAT IT SAYSWHAT THE CALLER DOES2xxDone, or acceptedProceed; for 202,poll for the outcome400, 422Your request is wrongDo not retry.Fix the caller.401, 403Who you are, orwhat you may doDo not retry.Refresh or escalate.409Conflicts withcurrent stateRe-read state,then decide429Slow downRetry after theRetry-After wait502, 503, 504Something upstreamfailed or timed outRetry with backoff,only if idempotent
The useful question is not what the code means but what the caller should do next. A 500 sits with the upstream group: report it with a correlation ID and retry only idempotent operations.

The codes that matter operationally

400 Bad Request and 422 Unprocessable Content mean the caller sent something invalid. Retrying wastes capacity and fills logs. The right response is to record the error with enough detail to fix the caller and, where a person is involved, show them what to correct.

401 Unauthorized and 403 Forbidden are often confused. 401 means "I do not know who you are", usually an expired or missing token, and the caller should refresh credentials once and then stop. 403 means "I know who you are and you may not do this". Retrying a 403 never helps. It should surface as a permissions problem to someone who can fix it.

404 Not Found sometimes means "this does not exist" and sometimes means "you may not know whether this exists". Many APIs return 404 instead of 403 for records the caller may not see, to avoid confirming that the record exists. Both are legitimate designs; mixing them within one API is not.

409 Conflict means the request clashes with the current state, such as an order that has already been cancelled. The caller should re-read the current state and decide again, not retry blindly.

429 Too Many Requests is a direct instruction to slow down. Well-behaved servers include a Retry-After header saying how long to wait. Well-behaved clients honour it. When Marula's gateway started returning 429 to a marketplace partner that synced too often (see Part 1), the partner's client retried immediately in a tight loop, which made the problem worse. Honouring Retry-After turned an outage-shaped problem into a slightly slower sync.

500 Internal Server Error means something unexpected broke. It might be transient, or it might fail the same way every time. Retry cautiously, once or twice with backoff, and only for idempotent operations.

502 Bad Gateway, 503 Service Unavailable and 504 Gateway Timeout usually mean something between the caller and the real work failed or was too slow. These are the most retryable codes, with three conditions: the operation must be safe to repeat, retries must back off with increasing waits and some randomness, and there must be a limit.

Idempotency decides whether a retry is safe

An operation is idempotent if doing it twice has the same effect as doing it once. Reading a product is idempotent. Setting a delivery address to a specific value is idempotent. "Capture R1,849 from this card" is not, unless the caller sends an idempotency key that lets the server recognise the second attempt as a duplicate.

That is why the retry decision cannot be made from the status code alone. A 503 on a catalogue read can be retried freely. A 503 on a payment capture can only be retried if the request carries the same idempotency key as the first attempt. Part 3 covers idempotency keys in more detail.

Who owns the retry?

Retries become dangerous when several layers each decide to retry on their own. If the browser retries three times, the gateway retries three times and the order service retries three times, one failed click can become 27 calls to an already struggling payments provider. That is a retry storm, and it turns a brief upstream wobble into a prolonged outage.

The fix is to decide, for each call path, which single layer owns retries and to make the others fail fast. The same problem is sharper in AI systems, where application code, an agent framework, a model gateway and the model provider can all retry. We cover that in The Model Gateway Is Not the AI Platform.

Every failure needs a correlation ID

One correlation ID, carried through every hopBrowserAPI gatewayOrdersPaymentsproviderWarehouseX-Correlation-ID: mc-7f3a9 (illustrative) in every request and every log line
When the warehouse returns a 503, support can find the shopper's request, the gateway entry and the order in seconds, because every log line shares one ID.

When a customer phones to say checkout failed, support needs to find that exact request across the browser, the gateway, the order service and whichever upstream system returned the error. A correlation ID, generated at the edge and passed on every downstream call and written into every log line, makes that a search instead of an investigation.

Return it to the caller too. An error page or API response that includes "reference mc-7f3a9" lets a customer, partner or support agent hand you the exact key to the logs. It costs nothing and saves hours.

A short client policy

  1. Never retry 4xx responses, except one credential refresh after a 401.
  2. Honour Retry-After on 429 and 503.
  3. Retry 502, 503 and 504 with exponential backoff and jitter, at most a few times.
  4. Retry anything that changes state only with an idempotency key.
  5. Assign one retry owner per call path; everyone else fails fast.
  6. Propagate and log a correlation ID on every call, and return it on errors.
  7. Alert on rising 5xx and 429 rates, not on individual failures.

Status codes are a small, old, well-understood part of the web. Treating them as operational instructions rather than trivia is one of the cheapest reliability improvements available.

Next: Part 6, Architecture diagrams should be evidence, not artwork. All parts are on the series page.

How CloudNala can help

We review API clients and integrations for retry behaviour, idempotency and error handling, and help teams set one retry policy per call path. For organisations with many partner integrations, we help define the error and correlation conventions that make support calls short.


Work with CloudNala

CloudNala helps organisations move from technology ambition to practical execution across cloud, AI, data, platform engineering and digital services.

Whether you are exploring AI, modernising your cloud environment, building a public-sector digital service, or turning an idea into a working MVP, we can help you shape the roadmap and deliver the next step.

Request an Architecture Review or write to us at consult@cloudnala.co.za