Graceful Degradation
Graceful degradation means: when a dependency fails, serve partial functionality instead of crashing. A page that loads with stale recommendations is better than a page that doesn't load at all. The pattern requires identifying which features are essential vs optional, having fallbacks for each, and making sure the fallback path is actually exercised.
Foundational.
How it works
Graceful degradation is the pattern of designing your system so that partial failures produce partial functionality, not full outages. The canonical example is Netflix: if the recommendation service is down, the homepage still loads — it just shows popular titles instead. The user doesn't see an error; they see a slightly worse homepage. The recommendation service can be down for hours and most users never notice.
This contrasts with fail-fast (circuit breaker), where the caller knows the service is down and gives up. Degradation goes one step further: the caller has a fallback ready — cached data, default values, a simpler computation — so the user-facing outcome is still acceptable.
The hardest part of graceful degradation is deciding what's essential. A useful framework:
- Critical path: things the user must have. For e-commerce: browse catalog, add to cart, checkout. These cannot degrade.
- Important but optional: things that materially affect UX. Product reviews, related products. Degrade to cached or hide.
- Nice-to-have: personalization, recommendations, A/B variants. Degrade to defaults or remove.
For each optional feature, you need a fallback that is independently reliable. A fallback that depends on the same database as the failed service is useless. Good fallbacks: cached responses from a CDN, default values baked into the binary, a separate lightweight service.
The art is in not over-degrading. Showing 'popular titles' is fine; showing an empty page or a 500 is not. The fallback should preserve the core promise of the page.
Common fallback strategies, in increasing order of complexity:
- Cached response: serve the last successful response. Works for read-heavy data (catalog, prices).
- Default value: substitute a sensible default (empty list, 'popular' instead of 'recommended').
- Stale-but-acceptable: serve data older than freshness requirements would normally allow.
- Reduced functionality: hide the broken widget entirely; the rest of the page works.
- Recompute locally: a simpler, more expensive version of the computation that doesn't need the failed dependency.
Avoid the trap of fallbacks that fail in the same way as the primary. If the recommendation service fails because Redis is down, don't write a fallback that also uses Redis.
If your fallback path is never exercised in production, it almost certainly doesn't work. Code paths that never run rot: the cache schema drifts, the default value is stale, the simplified computation has a bug. The Netflix Chaos Monkey approach is to deliberately fail dependencies in production so the fallback path is regularly exercised. If you can't do chaos engineering, at minimum run integration tests that inject dependency failures and assert the fallback works.
Anti-patterns to avoid:
- The error page as fallback: technically a fallback, but it gives the user nothing. Always prefer a degraded but useful response.
- The null fallback: returning
nullor an empty list silently. Sometimes correct, but the user sees a broken-looking page. Either hide the section or show something useful. - The slow fallback: the fallback path should be faster than the primary, not slower. A fallback that hits the same DB just re-creates the problem.
- Silent failures: if you degrade, log it and emit a metric. You need to know which features are running in degraded mode and for how long — otherwise you won't notice when a 'temporary' degradation becomes permanent.
Your e-commerce product page calls: catalog, pricing, inventory, reviews, recommendations. Inventory is down. What's the best degradation?
Pick one answer.
You wrote a fallback that serves the last successful recommendation response from cache. Why might this still fail in an incident?
Pick one answer.
Engineering mental model
Mental model. Think of Graceful Degradation as a deliberate boundary in a system. The boundary exists because something becomes harder to manage when everything is done in one place: latency, scale, failure isolation, consistency, cost, or team ownership. The useful question is not “what does Graceful Degradation mean?” but “what pressure makes this boundary worth introducing, and what new failure mode does it create?”
Before choosing Graceful Degradation, name the workload, the critical user path, the dominant bottleneck, the failure you are trying to absorb, and the trade-off you are willing to accept. If you cannot name those five things, the design is probably premature.
// Pseudocode
request = receive()
result = graceful_degradation(request)
return result
// Production questions:
// 1. What happens on timeout?
// 2. Can this operation be retried safely?
// 3. What is the bottleneck?Back-of-the-envelope reasoning
Numerical lens: write down traffic, payload size, read/write ratio, peak multiplier and durability target before choosing a component. The numbers should justify the architecture.
Interactive thought experiment: Graceful Degradation
Change the variables below and predict what breaks first in Graceful Degradation. The production lab can later reuse these same inputs.
Change one variable at a time. Predict the failure mode first, then move the slider and see whether your mental model matches the simplified system response.
If you are stuck on Graceful Degradation, start by drawing the request path and marking every network hop, stateful component, queue, cache and failure boundary. Then estimate where the system will saturate.
You increase traffic by 10× in a system using Graceful Degradation. What should you inspect first?
Pick one answer.
Which statement is the safest engineering habit when using Graceful Degradation?
Pick one answer.
You have dashboards for traffic, latency, errors and saturation. You can change the architecture, but every change has operational cost.
Production scenario: your system uses Graceful Degradation, traffic suddenly spikes, and p99 latency doubles. What is your first move?
Interview drill
Answer this without notes: When would you choose Graceful Degradation, and when would you intentionally avoid it? Mention at least one bottleneck it addresses, one failure mode it introduces, and one alternative. Then quantify the workload you are designing for.
A useful engineering lens for Graceful Degradation: define the problem it solves, the simpler design that fails first, the constraint that forces you to introduce this concept, and the new failure modes the concept creates.
Numerical sanity check
Back-of-the-envelope reasoning beats fake precision. State your traffic, payload, concurrency and growth assumptions explicitly, then calculate enough to know whether the current architecture is orders of magnitude away from the target.
Do not optimize for a memorized definition. Reason from the workload and failure mode.
Imagine the simplest version of a system using Graceful Degradation. What breaks first as traffic grows by 10×, and what would you change before reaching 100×?
Pick one answer.
What you gain, what you pay
- +Keeps the product usable during dependency failures.
- +Decouples availability from any single dependency.
- +Buys time for engineers to fix the underlying problem without users seeing an outage.
- +Forces explicit decisions about which features are critical.
- −More code paths to write and maintain (every optional feature needs a fallback).
- −Fallbacks that aren't tested rot and fail when needed.
- −Users may notice degradation and complain — silent failures can mask real problems.
- −Increases operational complexity: which features are degraded right now?
How this breaks in production
- Fallback depends on the same broken dependency — fails the same way.
- Fallback cache is cold at the moment of failure — nothing to serve.
- Silent degradation that no one notices — a 'temporary' fix becomes permanent.
- Over-degradation: every minor failure produces a stripped-down UI that feels broken.
Don't fall into these traps
- •Writing fallbacks but never exercising them in production.
- •Choosing fallbacks that share dependencies with the primary path.
- •Returning null/empty silently instead of hiding the section or substituting a sensible default.
- •Not logging or alerting on degradation — you don't know it's happening.
Real systems using this
How real systems implement this
- Netflix — The homepage calls ~10 microservices. If recommendations fail, it serves popular titles from a fallback. If personalization fails, it serves a generic layout. The user rarely notices.
- Amazon — Product pages have multiple widgets (reviews, related, sponsored). Each has an independent timeout and fallback. A slow widget is hidden; the rest of the page renders normally.
Practice saying it out loud
- Q1What is graceful degradation? How does it differ from fail-fast?
- Q2How do you decide which features can degrade and which can't?
- Q3Why is caching often the easiest fallback, and what are its failure modes?
- Q4How do you make sure fallbacks actually work when needed?
Further reading & references
Core explanations are original NO CAP material. External references are provided for deeper study and standards.
What next?
Mark as understood once the mental model clicks.
Next recommended
Circuit Breaker