Module 2 / Start with a simple system / Error handling and validation

Lesson9 minModule 2 · lesson 7 of 7

Error handling and validation

Expect bad input and partial failure.

In a real system, things go wrong constantly. Users type nonsense, phones lose signal mid-request, the database is briefly unreachable, a payment provider times out. A system that only plans for the happy path looks fine in a demo and breaks in production. Designing the unhappy path is part of the job, and it starts at the edges.

Validation means checking input before acting on it. Check the type (a quantity is an integer), range (between 1 and 20), length, format and, importantly, business rules (the restaurant is open; the coupon has not expired). Reject bad input early with a clear 400 or 422 and a message the client can show, listing which field failed and why. Validate on the server always; client-side checks are only a convenience for the user.

{
  "error": {
    "code": "validation_failed",
    "message": "The order could not be created.",
    "fields": [
      { "field": "items[0].quantity", "problem": "must be between 1 and 20" }
    ],
    "requestId": "r-77c1"
  }
}
A consistent error shape lets every client handle failures the same way. The requestId helps support find the log line.

Not all errors are the user's fault. Distinguish client errors (4xx: fix your request, retrying will not help) from server errors (5xx: our fault, maybe retry). Never leak internals such as stack traces or SQL in the response; log them privately instead, with a request id tying the two together.

ClientAPIPayment providerLogplace ordercharge cardtimeoutlog withrequest id503 try again
Handling a failure that may be temporary.

When the failure is temporary, such as a timeout, retrying helps, but carelessly it makes things worse. If a thousand clients retry instantly after an outage, they can crush a recovering server. The usual remedy is exponential backoff with jitter: wait 1 second, then about 2, then 4, each time adding a random amount so clients spread out, and give up after a few attempts. Retrying is only safe for idempotent operations, which is why idempotency keys from the API lesson matter. Also set timeouts on every outbound call, because a call with no timeout can hang forever and use up your server's threads.

A sound pattern for any request

Step 1 of 5

Validate

Reject bad input early with a precise 4xx.

Question 1

A request has quantity -3. Best response?

Question 2

Why add jitter to retry delays?

Your turn

A payment call times out. List what the server should do and one thing it must not do.

Stuck? Think about uncertainty: did the charge happen?

What to remember

  1. Validate input on the server: type, range, format and business rules.

  2. Use 4xx for client errors and 5xx for server errors, with a consistent error shape.

  3. Never leak internals; log them with a request id.

  4. Retry with backoff and jitter, only when safe, and always use timeouts.

  5. Do not swallow errors silently.