Background
Sections
IntroductionRequirements & Problem AnalysisConstraints & AssumptionsEstimation TechniquesFunctional vs Non-Functional RequirementsMoSCoW PrioritizationSystem Design FundamentalsArchitecture DiagramClass DiagramComponent DiagramData Flow Diagram (DFD)ER Diagram (Entity-Relationship Diagram)High Level Design (HLD)Low Level Design (LLD)Sequence DiagramState DiagramUse Case DiagramData StorageDocument StoresFile StorageGraph DatabasesIn-Memory DatabasesKey-Value StoresNewSQLNoSQL DatabasesObject StorageSQL Databases (RDBMS)Time-Series DatabasesWide-Column StoresDatabase ConceptsACID PropertiesCAP TheoremConsistency ModelsIndexingNormalization & DenormalizationReplicationSharding & PartitioningTransactions & Isolation LevelsScalabilityAuto-Scaling & ElasticityConsensus & Leader ElectionLoad BalancingReplication & Read ReplicasSharding & PartitioningVertical vs Horizontal ScalingAvailability & ReliabilityBackup & Data DurabilityCircuit BreakerData ConsistencyDisaster RecoveryFault Tolerance & FailoverGraceful DegradationHigh AvailabilityNetworkingCDNDNSFirewalls & VPNHTTP & HTTPSLoad Balancer & Reverse ProxyTCP/IP & UDPWebSocketsCachingCache InvalidationCache Read/Write PatternsCaching LayersEviction PoliciesRedis vs MemcachedMessaging & CommunicationDead-Letter QueueIdempotencyKafka vs RabbitMQ vs SQSMessage QueuesPub/SubCompute & ServicesAPI GatewayContainers & OrchestrationMonolith vs MicroservicesServerlessService DiscoveryService MeshWeb Server & App ServerAPI DesignAPI Versioning & IdempotencyAuthentication & AuthorizationGraphQLgRPCPaginationRate Limiting & ThrottlingRESTSecurityAuthentication & AuthorizationData PrivacyEncryptionInput Validation & InjectionOAuth2 & JWTSecrets ManagementXSS & CSRFStorage & File SystemsBackup & RetentionBlock vs File vs Object StorageData Lakes & WarehousesDistributed File SystemsEphemeral StorageObservability & MonitoringDistributed TracingHealth ChecksLoggingMetricsSLI, SLO, SLADesign PatternsBulkhead PatternCircuit Breaker PatternCreational PatternsRate Limiter PatternRetry PatternStructural & Behavioral Patterns

Graceful Degradation

7 min read

In a Nutshell

Graceful degradation is the principle that when part of your system fails or gets overloaded, it should lose functionality gracefully — shedding non-essential features while keeping the core experience working — rather than crashing entirely. A video site whose recommendation engine is down should still play videos; an e-commerce site whose review service is down should still let people buy. The goal is to make failures partial and invisible instead of total and catastrophic. It's the difference between "some features are temporarily unavailable" and "the whole site is down."

2D minimalistic diagram showing a web page with several feature panels: the core content panel (video player / product + buy button) stays fully rendered and healthy, while secondary panels (recommendations, reviews, related items) are greyed out or replaced with simple placeholders, illustrating the app still working with reduced functionality

How It Actually Works

Identify Critical vs Non-Critical Paths

Graceful degradation starts with ranking features by how essential they are. When resources are scarce or dependencies fail, sacrifice the bottom of the list to protect the top:

CRITICAL (never sacrifice):
  Checkout / payment  •  Video playback  •  Login  •  Send message

DEGRADABLE (drop under stress):
  Recommendations  •  Reviews  •  "People also viewed"
  Real-time notifications  •  Personalization  •  Analytics beacons

The design question for every dependency is: "If this is down, what's the least-bad experience we can still deliver?"

Degradation Techniques

Technique How It Degrades Example
Serve stale/cached data Use last-known-good instead of live Show cached prices when pricing service is down
Feature toggles / kill switches Turn off expensive features under load Disable personalized feed → show generic feed
Default/empty responses Return a safe placeholder Empty recommendations rather than an error
Reduced fidelity Serve a cheaper version Lower video resolution; text-only mode
Async deferral Queue non-urgent work for later Accept the order now, send confirmation email later
Load shedding Reject a fraction of low-priority requests Drop analytics calls to protect checkout

Load Shedding: Degrading Under Overload

When a system is overloaded (not failed — just too much traffic), the reliable move is to shed load deliberately: reject some requests quickly so the rest succeed, rather than accepting everything and collapsing.

Overloaded system, two choices:
  ❌ Accept all requests → all get slow → all time out → 0% success
  ✅ Shed 30% (fast reject low-priority) → 70% succeed fully

Prioritize by request importance:
  Tier 1: checkout, payments      → always admit
  Tier 2: browsing, search        → admit if capacity
  Tier 3: analytics, prefetch      → shed first

This connects directly to backpressure and rate limiting — the system protects itself by saying "no" fast rather than "maybe" slowly.

Graceful Degradation vs Fault Tolerance vs Circuit Breaker

Concept Focus
Fault tolerance Keep working correctly despite component failure (redundancy/failover)
Circuit breaker Mechanism to stop calling a broken dependency
Graceful degradation Strategy for what the user experiences when something is unavailable

They compose: a circuit breaker trips on a failing dependency (mechanism), and the fallback it returns is the graceful degradation (user experience). Fault tolerance tries to prevent the failure from being visible at all; degradation makes it survivable when it is.

Designing for Degradation

  • Make features independent. A failure in reviews must not be able to break the product page. Isolate with timeouts, circuit breakers, and separate deployments.
  • Fail open vs fail closed. Decide per feature: a recommendations failure should fail open (show generic content); an authorization check should fail closed (deny access) for safety.
  • Test degraded modes. Regularly disable dependencies (chaos engineering) to verify the degraded experience actually works — degraded paths rot silently if never exercised.

2D minimalistic diagram showing load shedding under overload: incoming requests of three priority tiers flowing toward a service at capacity; a gate admits all Tier-1 (checkout) requests, admits some Tier-2 (browse) requests, and rejects Tier-3 (analytics) requests with fast "429" responses, keeping the service healthy

Seeing It in Action

Scenario: A product page that degrades gracefully when its dependencies fail.

def render_product_page(product_id, user_id):
    page = {}

    # CRITICAL: must succeed, or we show an error page
    page["product"] = product_service.get(product_id)   # no fallback — core

    # DEGRADABLE: each wrapped so its failure can't break the page
    page["price"] = safe(lambda: pricing_service.get(product_id),
                         fallback=lambda: cached_price(product_id))   # stale ok

    page["reviews"] = safe(lambda: review_service.get(product_id),
                           fallback=lambda: [])          # empty is fine

    page["recommendations"] = safe(
        lambda: rec_service.get(user_id, product_id),
        fallback=lambda: popular_items())                # generic fallback

    return page

def safe(fn, fallback):
    """Run fn with timeout + circuit breaker; on any failure, use fallback."""
    try:
        return with_timeout_and_breaker(fn, timeout=0.3)
    except Exception:
        log_degradation(fn)          # alert so we know we're degraded
        return fallback()

What the user sees when reviews and recommendations are both down: a fully functional product page with the item, a (possibly cached) price, and a working "Buy" button — just without reviews and with generic recommendations. They can still complete the purchase. Compare that to the naive version where any one failed dependency throws a 500 and the entire page is lost.

The essential discipline: log every degradation so the system reports "I'm running degraded" — otherwise you achieve invisible degradation and invisible outages, and never fix the root cause.

Interview Questions

  1. Q: What is graceful degradation and why is it valuable? Hint: It's designing a system to shed non-essential functionality while keeping the core experience working when parts fail or overload, making failures partial and often invisible instead of total. Value: users can still do the important thing (buy, watch, message) during a partial outage, dramatically improving perceived reliability and protecting revenue.

  2. Q: How do you decide what to degrade? Hint: Rank features by how critical they are to the core user journey. Protect critical paths (checkout, playback, auth) and sacrifice degradable ones (recommendations, reviews, personalization, analytics) first. For each dependency ask: "if this is down, what's the least-bad experience we can still deliver?"

  3. Q: What is load shedding, and why is it better than accepting all requests under overload? Hint: Load shedding deliberately rejects some (low-priority) requests quickly so the remainder succeed. If you accept everything under overload, all requests get slow and time out → ~0% success. Shedding a fraction fast keeps the majority (and the highest-priority traffic) succeeding — say "no" fast rather than "maybe" slowly.

  4. Q: Explain fail-open vs fail-closed with an example, and how you choose. Hint: Fail-open = on failure, allow/continue with a fallback (recommendations down → show generic content) — good for availability of non-critical features. Fail-closed = on failure, deny (auth service down → deny access) — good for security/safety. Choose based on whether an incorrect "allow" or an incorrect "deny" is more harmful.

  5. Q: How does graceful degradation relate to circuit breakers and fault tolerance? Hint: They compose. Fault tolerance tries to keep failures invisible via redundancy/failover. A circuit breaker is the mechanism that detects a broken dependency and fails fast. Graceful degradation is the user-facing strategy — the fallback the breaker returns (cached data, defaults, generic content) that keeps the experience usable.

References

Dive Deeper