Knowledge Guide
HomeSystem DesignMicroservices Patterns

Use Cases and System Design Examples

A retry works because most failures between services are transient — a dropped packet, a 503 during a deploy, a lock timeout — so re-issuing the same request a moment later often lands on a healthy path; the craft is choosing a delay that grows and is randomized so a whole fleet of clients doesn't re-collide, and guaranteeing the request is safe to repeat.

That single sentence hides two hard constraints that every real implementation below respects: only retry idempotent operations (or make them idempotent), and only retry transient errors. Break either and retries stop being a safety net and become an amplifier of the outage.

A traced example: charging a card through a flaky gateway

An e-commerce backend charges an order via POST /v1/charges. The gateway is mid-deploy and returns 503 Service Unavailable on the first two tries. The client uses full-jitter exponential backoff — the same strategy AWS and Google Cloud SDKs ship — with base = 200ms, maxDelay = 8s, and delay = rand(0, min(maxDelay, base × 2^n)), and it sends the same Idempotency-Key on every attempt (Stripe / PayPal style).

AttemptClockBackoff windowDrawn delayResponse
1t = 0 msmin(8000, 200×2^0) = 200 ms137 ms503 — transient, retry
2t = 137 msmin(8000, 200×2^1) = 400 ms291 ms503 — transient, retry
3t = 428 ms200 OK — ch_1Nz…

Outcome: the customer sees success after 428 ms instead of an error, and exactly one charge exists. The idempotency key is what makes attempt 3 safe: if the gateway had actually processed attempt 1 but the 200 response was lost on the wire, the server would recognize order_88213 and return the original charge rather than creating a second one.

diagram
diagram

The correct retry loop (and why the naive one is dangerous)

The core of every implementation above is small. This is the whole strategy, compile-checked:

// Do retries fn up to maxAttempts using full-jitter exponential backoff.
// fn MUST be idempotent (or guarded by an idempotency key). retryable
// reports whether an error is transient and worth retrying at all.
func Do(maxAttempts int, base, maxDelay time.Duration, fn func() error, retryable func(error) bool) error {
    var err error
    for attempt := 0; attempt < maxAttempts; attempt++ {
        if err = fn(); err == nil {
            return nil
        }
        if !retryable(err) || attempt == maxAttempts-1 {
            return err // give up on permanent errors or the last try
        }
        exp := float64(base) * math.Pow(2, float64(attempt))
        window := math.Min(exp, float64(maxDelay)) // cap growth
        time.Sleep(time.Duration(rand.Int63n(int64(window) + 1)))
    }
    return err
}

Why the naive version is wrong. The tempting one-liner — for i := 0; i < 3; i++ { if fn() == nil { break }; time.Sleep(time.Second) } — fails three ways at once: (1) it retries every error, so a 400 Bad Request or a validation failure burns three attempts and three seconds to reach the same permanent error; (2) it has no jitter, so 10,000 clients that all failed at the same instant retry in lockstep exactly one second later — a self-inflicted DDoS the moment the downstream comes back; and (3) it assumes fn is safe to repeat, which for a bare POST /charges without an idempotency key means a lost response becomes a double charge. The correct loop gates on retryable, randomizes the delay, and the surrounding contract requires idempotency.

Where this shows up in real systems

Pitfalls

When to use it — and when NOT to

Reach for retries when failures are transient and independent (occasional timeouts, throttling, brief unavailability during a deploy) and the operation is idempotent or can be made so. The concrete signal: your dashboards show a low, spiky error rate that clears on its own within seconds — not a sustained failure.

Trade-offs vs. named alternatives:

Choose retries when the error is transient, independent, and the call is idempotent; prefer a circuit breaker when failures are sustained and correlated; prefer a dead-letter queue when the work is asynchronous and can be deferred; prefer fail-fast when latency matters and the operation cannot be made safe to repeat.

Takeaways


Re-authored/Deepened for this guide. Sources: Marc Brooker, “Exponential Backoff and Jitter” (AWS Architecture Blog); AWS SDK and Google Cloud client-library retry documentation; Stripe API “Idempotent Requests” documentation; Michael T. Nygard, Release It! (2nd ed., retry, circuit breaker, and bulkhead stability patterns); Gregor Hohpe & Bobby Woolf, Enterprise Integration Patterns (Dead Letter Channel); Netflix Hystrix / resilience4j resilience patterns.

🤖 Don't fully get this? Learn it with Claude

Stuck on Use Cases and System Design Examples? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Use Cases and System Design Examples** (System Design) and want to truly understand it. Explain Use Cases and System Design Examples from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Use Cases and System Design Examples** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Use Cases and System Design Examples** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Use Cases and System Design Examples** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes