Keep Informed and Carry On
User experience under failure is about preserving agency without hiding reality.
When something breaks, a professional product does not abandon the user. It explains what changed, protects their work, and lets them continue using the parts that are still safe.
Sometimes that means carrying on in a reduced mode. Sometimes it means stopping clearly because continuing would risk data loss, duplicate work, or a false promise.
What Still Works? #
- Serve a reduced experience instead of an error page.
- Prioritize the core path; treat extras as optional.
- Disable only the unsafe action, not the whole screen.
- Make stale data visibly stale.
If checkout is down, account history, invoices, saved carts, and support may still work. If recommendations are down, search can still work. If a shipment is delayed, the user should still be able to see the order, update contact information, or contact support.
Is It Game Over? #
Some failures are total for now. If AWS is down, payment processing is unavailable, or the warehouse cannot accept orders, pretending otherwise wastes the user's time.
Say so plainly and early:
- What is unavailable?
- Is the user's work or order safe?
- What can they still do?
- Will you update them, or should they check back?
"Checkout is temporarily unavailable. Your cart is saved. We'll update this page when checkout is back." is better than a spinner that never ends.
Carry on when the remaining path is safe. Stop clearly when continuing would mislead the user or risk their data.
Preserve the User's Work #
- Save drafts locally when the network drops.
- Queue safe actions for later sync.
- Keep scroll position, form input, uploads, and playback position where possible.
- Confirm when recovery completes.
The best recovery gets out of the way. "Reconnecting..." should disappear when the connection returns. "Saved locally" should become "Saved" when sync succeeds.
Resilience Patterns #
- Timeouts — never wait forever for a dependency.
- Circuit breakers — stop calling a failing dependency; probe to recover.
- Bulkheads — isolate resources so one failure can't sink the rest.
- Fallbacks — cached/default/stale responses when live data fails.
- Load shedding & backpressure — reject early rather than queue forever.
The Circuit Breaker, Visualized
A circuit breaker has three states. It fails fast while a dependency is down, then uses a single probe to notice recovery — the "detect recovery, get out of the way" idea made mechanical.
+--------+
| CLOSED | normal traffic
+--------+
|
| trip: too many failures
v
+--------+
| OPEN | fail fast during
+--------+ a cooldown window
|
| cooldown elapsed
v
+-------------+
| HALF-OPEN | allow ONE
| try 1 call | probe call
+-------------+
|
+--> worked -> back to CLOSED
+--> failed -> back to OPEN
CLOSED : let calls through
OPEN : short-circuit; don't
even try (fail fast)
HALF-OPEN : a single probe decides
whether it recovered
Fallback Sources #
- Serve stale cache (stale-while-revalidate).
- Precomputed defaults or last-known-good values.
- Make it obvious to the user when data may be stale.
Recovering Cleanly #
The third part of the rubric is the one most systems forget: what happens after things heal.
Recovery is not complete when a request finally succeeds. The system must get back to a truthful state.
- Detect recovery, don't just detect failure. Probe the dependency (this is what a circuit breaker's half-open state is for) and notice the moment it's healthy again.
- Resume where you left off. Keep enough state — buffered data, scroll position, the user's place — to continue rather than restart. This is why YouTube resumes instead of reloading.
- Retract the messaging. "Reconnecting..." should vanish the instant you're connected. A stale error banner after recovery is its own bug.
- Reconcile quietly. Optimistic UI, offline queues, and stale caches all need to converge back to the truth without a jarring reset. See Do or Do Not. Then Retry. for doing this safely and idempotently.
- Repair stuck states. Run repair jobs for states like
paid_waiting_for_fulfillment, abandoned uploads, or shipments that never received a final carrier update.
- Tell the user only when it affects what they can do. If the system recovered silently, stay out of the way. If the delay blocks access, delivery, or payment, say so.
For physical delivery, recovery may mean a replacement shipment, a refund, or a support workflow. That is still failure handling. The recovery path must be as intentional as the retry loop.
Front End vs. Back End Notes #
- Front end: skeleton states, offline support, optimistic UI with reconciliation, stale markers, saved drafts, and precise disabled states.
- Back end: circuit breakers around dependencies, cached fallbacks, load shedding, durable queues, and clear status endpoints so clients can tell the truth.
Related chapters: Whose Fault Is It Anyway? · Do or Do Not. Then Retry. · Writing Insanely Great Error Messages