Part 7 · Reliability & Resilience

Graceful Degradation & Load Shedding

At 5× your planned peak, trying to serve everyone means serving no one. How to fail partially and on purpose: rank features and switch off the optional ones, reject excess work early and cheaply, shed by priority, and use waiting rooms for spikes you can see coming.

Lesson 448 min readintermediate#resilience

On this page

  1. The Problem
  2. The Core Idea
  3. How It Works
  4. Trade-offs
  5. In the Real World
  6. Interview Questions
  7. Key Takeaways
  8. Further Reading

The Problem

It is the biggest sale of the year. Traffic is 5× your planned peak (the busiest moment you planned for). Every server uses 100% of its CPU, the database is saturated (full, with no room for more work), and response times climb from 200 ms to 30 seconds. Soon nobody can do anything: browsing, searching, adding to cart and paying all fail.

Here is the sad part. Suppose the system had refused 30% of requests quickly, or switched off heavy features like personalised recommendations. Then the other 70% of users could have shopped and paid normally.

When overload cannot be avoided, trying to serve everyone usually means serving no one well. Graceful degradation means turning off or simplifying less important features so the system keeps working. Load shedding means refusing some requests on purpose. Both are about failing partially, on purpose, so the most important things keep working.

The Core Idea

Graceful degradation is like a restaurant on a very busy night. The kitchen cannot make the full 80-item menu for everyone. So the manager switches to a short menu of the 10 most popular dishes that are quick to cook. Customers get a slightly simpler experience, but everyone still eats.

Load shedding is like a hospital emergency department during a disaster. When patients arrive faster than doctors can treat them, staff triage. Triage means sorting by how urgent each case is. Critical patients are treated first. People with minor injuries are asked to wait or go elsewhere. Turning some people away early is better than letting everyone wait until it is too late.

Both ideas accept a hard truth: your capacity is limited, so decide in advance what matters most.

How It Works

Part 1: Graceful degradation

Decide which features are critical and which are nice-to-have. Then make the nice-to-have ones optional when the system is under stress.

Example for an e-commerce site:

TierFeaturesUnder stress
CriticalLogin, product page basics, cart, checkout, paymentMust keep working
ImportantSearch, order history, stock statusKeep, maybe simplified
Nice-to-havePersonalised recommendations, reviews, "people also bought", live chat widget, animated bannersTurn off or replace with static versions

Degradation techniques:

  • Serve stale or cached data. Stale means a little old. A cache is a store of ready-made answers. Show cached prices on product lists (while checkout checks the real price again), cached search results, or yesterday's "popular items" instead of live personal picks (posts 15–16).
  • Simplify responses. Return fewer items per page, fewer fields and smaller images.
  • Turn off expensive features with feature flags or kill switches. A feature flag is a setting that turns a feature on or off without a new release. A kill switch is a flag made to turn something off fast. You can flip them by hand, or automatically when load or errors go above a limit.
  • Use static fallbacks. Show a pre-built page that a CDN serves. (A CDN is a network of servers that keeps copies of your pages near users.) For example, a ready-made homepage or product page.
  • Go read-only. If the database cannot take writes, let users browse but show "Ordering is paused for a moment" instead of failing at random.
  • Queue non-urgent work. Emails, analytics and notifications wait in queues (lines of jobs) and catch up later (post 18).
  • Use circuit-breaker fallbacks (post 42) for dependencies that fail. A dependency is another service that you call.

Plan for degradation from the start. When you build each feature, ask:

  • "What happens if this dependency is slow or down?"
  • "Can this page render without it?"
  • "Is there a flag to turn it off?"

A page that needs 12 services to all succeed is fragile. A page that skips 8 optional ones when they fail is resilient (it keeps working when parts fail).

  1. 1
    Normal
    full personalised experience
  2. 2
    Brownout
    lower image quality, hide “people also bought”
  3. 3
    Serve stale
    cached prices on listings, yesterday's popular items instead of live recommendations
  4. 4
    Static fallback
    CDN-served homepage and product pages
  5. 5
    Read-only
    browse works; “ordering is paused for a moment”
  6. 6
    Always protected
    login, cart, checkout, payment
A degradation ladder for a sale day — each rung is a flag you can flip. · flow

Part 2: Load shedding

Load shedding means rejecting some requests on purpose when a server is overloaded, so it can serve the rest well. To shed is to drop. It is like a ship that throws some cargo overboard to stay afloat.

Why reject, and not just queue? Suppose a server accepts more work than it can do:

  • requests queue up and latency (the time to answer) climbs (post 8),
  • clients time out (give up waiting) after, say, 5 seconds, but the server keeps working on those requests anyway. It wastes effort on answers that nobody will receive,
  • the timeouts trigger retries, which add even more load (post 41),
  • memory fills up, garbage collection (the automatic memory clean-up) runs all the time, and the server may crash. That makes things worse for everyone.

Rejecting early and cheaply (in microseconds, with a quick 503 and Retry-After) keeps the server in its efficient zone. The requests that it accepts finish quickly.

Without shedding
  • •Accepts everything; queues grow; latency climbs
  • −Clients time out while the server keeps working on their requests
  • −Timeouts trigger retries → even more load
  • −Congestive collapse: more load, less useful work
With shedding
  • •Rejects the excess in microseconds with 503 + Retry-After
  • +Accepted requests stay fast
  • +Throughput (work done per second) stays at the maximum
  • +Clients back off instead of piling on
What happens past capacity. · compare

This collapse is called congestive collapse: after some point, more load gives less useful work, not more.

When to shed

These signs tell you that a server is overloaded:

  • Concurrency (in-flight requests, which means requests being handled right now) above a limit. This is often the most reliable sign.
  • Queue length or queue wait time too high.
  • CPU or memory above a threshold (a set limit).
  • Latency rising above target.

Adaptive concurrency limits. Instead of a fixed number like "max 200 in-flight requests", some systems learn the right limit by themselves. This is similar to TCP congestion control (post 2), which is how the internet's main transport protocol slows down when the network is busy. The limit goes up while latency stays good, and goes down when latency climbs. Netflix's open-source concurrency-limits library and Envoy's adaptive concurrency filter work this way.

What to shed: prioritisation

Not all requests are equal. Label each request with a priority or criticality (how important it is), and shed the least important first:

1 · Critical
checkout, payment, login, load balancer health checks — never shed while anything else can be
2 · High
product pages, cart
3 · Medium
search, browsing, order history
4 · Low
recommendations, analytics events, prefetching
5 · Sheddable
background syncs, crawlers and bots (programs that scan sites), retries — first to go
Shed from the bottom up. Tag every request with a criticality. · layers
  • Requests that a user starts matter more than background ones (like a phone app that loads data early, called prefetching).
  • Retries can be shed before first attempts. They are already part of the problem.
  • Bots and crawlers go first.
  • Paying customers or critical tenants (customer accounts) may get priority, but be careful to stay fair.

Where to shed

  • At the edge or load balancer (the first machines that receive traffic): the cheapest place. Rejected requests never reach your servers.
  • At the API gateway (the single entry point to your services): by route or priority (post 13).
  • In each service: a small check at the very start of handling a request ("am I over my concurrency limit? Is this request low priority?").
  • In queues: drop or delay low-priority jobs when the backlog (the waiting jobs) grows (post 38).

Smart queue handling under overload

  • Bounded queues. A bounded queue has a maximum size. Never let a queue grow without a limit.
  • Drop requests that have waited too long. If a request sat in a queue longer than the client's timeout, the client has already given up, so do not process it. (This is sometimes called deadline-aware or "controlled delay" queueing.)
  • LIFO under overload. Normally a queue is FIFO (first in, first out, like a line at a shop). Some systems switch to LIFO (last in, first out, like a stack of plates) when overloaded. The newest requests are the ones whose clients are most likely still waiting. The old ones have probably timed out already.

Tell clients to back off

When shedding, respond with 503 Service Unavailable (or 429 for limits on one client) and a Retry-After header. Well-built clients (post 41) wait and retry with some random delay (jitter), and do not overload you.

Brownouts and waiting rooms

  • Brownouts: a brownout is a partial power cut that dims the lights but does not turn them off. In software, you turn down non-essential features for a while on purpose (like image quality or recommendations) to reduce load, before things become critical.
  • Virtual waiting rooms: use these for huge spikes that you can predict (concert ticket sales, product launches, big sales). Put users on a queue page ("You are number 18,402 in line") and let them into the site at a rate that the system can handle. This is load shedding with good manners.

Test it

As with failover (post 40), degradation that was never tested often fails when you need it:

  • load-test (send fake heavy traffic) beyond your peak to see how the system behaves when overloaded,
  • practise flipping kill switches,
  • check that fallback pages and read-only modes really work.

Trade-offs

  • Degradation: core features survive, but users get a simpler experience. Each fallback path needs to be built and tested.
  • Load shedding: the system stays healthy and fast for accepted requests, but some users see errors on purpose. You must choose priorities with care and fairness.
  • Static limits: simple, but they may become wrong when hardware or code changes. Adaptive limits: they tune themselves, but they are more complex and need good latency data.
  • Waiting rooms: controlled and fair during extreme spikes, but users must wait, and you need extra infrastructure.

In the Real World

Netflix's prioritised load shedding. Netflix wrote about adding priority-based load shedding at its API gateway. Requests are labelled by importance. For example, requests that start playback are more important than background prefetching or logging. When the system is stressed, low-priority traffic is shed first. So members can still press play, even during incidents.

Amazon's load shedding. The AWS Builders' Library article "Using load shedding to avoid overload" explains how Amazon services reject extra requests early, put important work first, and avoid wasting effort on requests whose clients have already timed out.

Google's criticality levels. Google's SRE book (SRE means site reliability engineering) describes labelling requests with criticality. The levels are CRITICAL_PLUS, CRITICAL, SHEDDABLE_PLUS and SHEDDABLE. During overload, backends reject the least critical requests first. The same book shows how overload leads to cascading failures (one failure causing the next) when this is not done.

Ticket sales and waiting rooms. Ticketing sites for concerts and big sporting events, and sites for sneaker drops and flash sales, commonly use virtual waiting rooms to let users in at a controlled rate. Without them, a massive rush can take the site down for everyone.

Big sale days in e-commerce. During major sales, large retailers often simplify pages, turn off heavy personalisation, rely a lot on content cached by a CDN, and protect checkout capacity. This is graceful degradation, planned weeks in advance.

Interview Questions

Q1What is graceful degradation? Give examples.

You reduce features on purpose under stress, so the core keeps working. Examples: turn off recommendations and reviews, serve cached or static pages, return fewer items, go read-only, or queue non-urgent work. Feature flags or kill switches control all of these.

Q2Why reject requests instead of queueing them during overload?

Queued requests wait longer than their clients' timeouts. So the server does work that nobody receives, latency climbs, and retries add more load. This is congestive collapse. Rejecting the extra requests early and cheaply keeps accepted requests fast and keeps the server in its efficient zone.

Q3What signals would you use to decide when to shed load?

In-flight concurrency over a limit (often the most reliable), queue length or queue wait time, latency above the target, and CPU or memory saturation. Adaptive concurrency limits learn the right cap from latency, much like TCP congestion control.

Q4What would you shed first on an e-commerce site?

Bots and crawlers, background syncs and prefetches, retries, analytics events and recommendation calls. I would shed all of these long before checkout, payment and login.

Q5How do you handle a known massive spike like a ticket sale?

Add capacity before the event, fill the caches and CDN in advance (pre-warm), and plan which features to switch off. Put a virtual waiting room in front that lets users in at a rate the system can handle. This stops the rush from taking the site down for everyone.

Key Takeaways

  • Under extreme load, trying to serve everyone often means serving no one. Fail partially, on purpose.
  • Graceful degradation: rank features by importance, and switch off or simplify non-critical ones (stale cache, static fallbacks, read-only mode, feature flags) to protect the core.
  • Load shedding: reject excess requests early and cheaply (503 + Retry-After) to avoid congestive collapse, using concurrency or latency limits, ideally adaptive.
  • Prioritise: shed bots, background work, retries and low-value features before checkout, payments and login.
  • Use bounded queues, drop requests that have already timed out, consider waiting rooms for planned spikes, and test it all.

Further Reading

  • The AWS Builders' Library article "Using load shedding to avoid overload"
  • Site Reliability Engineering by Google (chapters "Handling Overload" and "Addressing Cascading Failures")
  • The Netflix Tech Blog post "Keeping Netflix Reliable Using Prioritized Load Shedding"
  • Netflix's open-source concurrency-limits project on GitHub
  • Stripe's blog post "Scaling your API with rate limiters" (its sections on load shedders)

Published by Jainik · Last updated · How these lessons are made

Keep learning

Part of the study guide

Related series