Home

/

The Production-Ready Playbook

/

Timeout + Fallback

Timeout + Fallback

Chapter 7
Part II
3
min read

Timeout + Fallback

The problem: the ETA call hangs. Not fails, hangs, holding a thread indefinitely while the customer stares at a spinner and the next order piles up behind it.

A hang is worse than an error, because an error you can handle and a hang just consumes you. Every outbound call needs a timeout (Nygard, Release It!). Pair it with a fallback: when the ETA call times out or fails, return something useful instead of an exception, a default "30-45 min" estimate, a cached quote, a degraded-but-honest answer.

var resilientEta = new ResiliencePipelineBuilder<DeliveryEta>()
    .AddTimeout(TimeSpan.FromSeconds(2))
    .AddFallback(new FallbackStrategyOptions<DeliveryEta>
    {
        FallbackAction = _ => Outcome.FromResultAsValueTask(DeliveryEta.Default),
        ShouldHandle = new PredicateBuilder<DeliveryEta>()
            .Handle<TimeoutRejectedException>()
    })
    .Build();

What it buys you in production: bounded latency and a graceful answer. The customer gets a default ETA in two seconds instead of a blank screen in thirty, and the order still goes through. A bounded wait is the difference between a slow checkout and a dead one.

Skip-if: you genuinely have no acceptable fallback and a wrong answer would be worse than no answer. The charge itself is like that, you can't fall back to a "default" payment. Keep the timeout regardless; it's the fallback that's optional, not the bound on waiting.

Idempotency (What Makes Retries Safe)

The problem: you retried the charge, but you don't know whether the first attempt actually landed before the connection dropped, so retrying might bill the customer twice and place two Orders for one basket.

This is the pattern that licenses the first one. An operation is idempotent if doing it twice has the same effect as doing it once. Reads are idempotent for free. Writes are not, until you make them so, usually with an idempotency key: the customer app sends a unique token with the basket, you record it on first execution, and a retry carrying the same token returns the original Order instead of placing a second one (Stripe's API documents this pattern well, which is no accident; it's a payments problem first). The same key flows through to the payment gateway, so the charge is deduplicated end to end.

// On the order-placement path, keyed by the basket's token.
var existing = await _orderGateway.FindByIdempotencyKeyAsync(key);
if (existing is not null) return existing;        // replay, don't re-place

var order = await _orderGateway.PlaceAndRecordAsync(key, request);
return order;

What it buys you in production: safe retries. Without idempotency, retry is a loaded gun pointed at the customer's card; with it, the worst a duplicate request can do is return the same Order twice. Every at-least-once message broker (Ch 6) leans on this too, which matters once OrderPlaced fans out to the kitchen and courier-matching consumers.

Skip-if: the operation is already a pure read, or naturally idempotent (setting an order's status to a fixed value). Don't bolt a key store onto something that can't double-fire.

Retry is the promise. Idempotency is the thing that lets you keep it.

the-pareto-stack-cloud-design-patterns-for-small-teams
the-ladder-of-altitudes
how-to-read-this
object-level-the-patterns-that-earn-their-keep
decorator
state
component-level-structuring-one-service
ports-and-adapters-hexagonal
mediator-the-commandquery-split
data-persistence
optimistic-concurrency
messaging-scale
outbox
resilience-staying-up-when-dependencies-dont
rate-limiting-throttling
timeout-fallback
the-composed-pipeline
observability-diagnostics-seeing-inside-production
metrics-the-four-golden-signals
externalised-configuration
hosting-cloud-agnostic-by-default
sidecar-ambassador
orchestrator-agnostic-deploy
a-reference-service
the-relay-outbox-to-queue
the-payment-saga-charge-pay-out-compensate
the-over-engineering-tax
conclusion-production-ready-deliberately
the-pattern-quick-reference-card
altitude-3-data-persistence
altitude-5-resilience
the-skip-list
full-event-sourcing-for-crud
robert-c-martin-uncle-bob-the-house-authority-for-structure
altitude-2-component
altitude-4-messaging-scale
altitude-6-observability-diagnostics

Download the full PDF for free?

Free download — no account required

Get the PDF
Get the PDF
Related Chapters
Free Download
Get the full PDF
All pages, including all code examples, diagrams, and the appendix reference card.
No spam. Unsubscribe at any time.
Your email won't be shared.
Oops! There's a problem with your request. We're working on fixing it. Please try again later.