Skip to content
advanced Phase 12 · Resilience Patterns

Chaos Engineering and Game Days

Inject controlled failures in production to verify resilience claims.

55m
0 problems
Topic Progress 0%

Steady State First: What Separates a Chaos Experiment From Sabotage

"Let's kill a pod on Friday afternoon and see what happens" is not chaos engineering — it is a small outage you scheduled yourself. The discipline is older than the tools and is defined by one requirement: state a falsifiable steady-state hypothesis before you inject anything. Steady state is the normal behavior you can measure continuously — request success rate, latency percentiles, throughput, queue depth, error-budget burn. The experiment tests whether that steady state survives a specific fault. If you cannot say what "healthy" means in numbers, you cannot run an experiment; you can only break things and watch.

A hypothesis you could actually defend in a design review has six parts:

hypothesis:   Checkout success rate stays >= 99.5% and p99 < 800ms
fault:        Terminate one pricing pod (SIGKILL)
scope:        1 of 5 pods, region eu-west-1, synthetic user cohort only
expectation:  Breaker opens <10s, traffic reroutes, recovery <30s
abort_if:     Success rate < 99% for 60s OR page fires for the checkout service
verify:       Grafana "checkout SLO" board + synthetic probe at 1 Hz

Note what is absent: the word "should" doing vague work. Every clause is a number someone can be wrong about — which is the point. The experiment has three possible outcomes and two of them are valuable: the hypothesis holds (you have evidence, not confidence, that the safety property works), or it fails and you find the gap while a customer is not involved. The third outcome is the one practitioners fear most: the fault landed, real damage occurred, and your dashboards showed nothing — a null result that actually means your observability cannot see the failure. Chaos experiments validate detection as much as resilience; "did the right alert fire?" is a first-class success criterion.

Chaos also sits in a specific spot relative to the testing you already do, and interviewers ask for that map:

Practice What it varies What it proves
Unit / integration tests code paths correctness under known conditions
Load testing traffic volume capacity and latency curves
Chaos experiment injected failure your assumptions about failure behavior hold
Game day failure + humans your people, runbooks, and comms work

A staging environment is a reasonable place for early experiments, but it lies in three predictable ways: synthetic traffic is smaller and less varied, your staging topology has drifted from production (one replica instead of six, no cross-AZ failure modes), and nobody's real money is on the line so nobody reads the dashboards. The claims that matter — "the breaker opens under real load," "auto-scaling saves us," "the CDN absorbs an origin failure" — can only be verified where that machinery is actually running. Start there, with a tiny blast radius, which is the next chapter's subject.

Practically, begin by writing hypotheses about protections you already believe exist: "if the primary database fails, reads continue from the replica within 15 seconds," "if one availability zone loses power, capacity drops 40 % but p99 stays under target," "if the payment provider is unreachable, checkout degrades to the queue-and-reconcile path rather than erroring." Each hypothesis is a claim currently living in someone's head. The experiment converts it into a result, and every result goes into an experiment log — hypothesis, scope, window, outcome, follow-ups — so the organization accumulates a record of what has actually been verified instead of what is generally assumed.

Blast Radius and Kill Switches: Controlling the Experiment Before It Controls You

Every experiment has a blast radius: the fraction of real users affected, multiplied by how severely, multiplied by how long. Your entire job in production chaos is to make that product small, bounded, and instantly reversible — the experiment must have a smaller radius than the incident it is trying to prevent, or you have invented a more expensive way to cause the outage you were worried about.

Radius control starts with an escalation ladder you climb one rung at a time, gated by the previous rung's results:

1. Local / dev machine, synthetic traffic          → does the fault even trigger?
2. Staging, production-shaped topology             → do metrics and alerts see it?
3. ONE non-critical instance in prod, off-peak     → single user's request path
4. One availability zone, or one tenant cohort     → real failure domain
5. Multi-AZ or region-scale                        → only after 3 and 4 are boring

Never skip rungs because the change looks small. The failure mode of rung 4 is that your "non-critical" instance shares a database, a connection pool, or a message broker with checkout — dependency graphs are where blast radius leaks. Before injecting, confirm isolation explicitly: which services, tenants, and data stores can this target actually reach, and is the traffic you are about to disturb genuinely replacable?

Inside the run, four controls are non-negotiable:

  • An automatic abort, wired to the steady-state metrics. If success rate drops below the abort threshold for 60 seconds, the experiment terminates itself — no human decision, no "let's give it one more minute." During an incident your judgment is compromised, so the kill switch must be code, not a person. A manual big-red-button stop exists too, but as the backup to automation, not the primary.
  • A hard time box — often 10 minutes — that stops the experiment even if everything looks healthy. Open-ended experiments drift.
  • A prepared rollback that has been tested before the day. kubectl rollout undo, a feature flag off, chaos-mesh experiment deletion, an infrastructure restore command. An untested rollback is a hypothesis about recovery you have not run.
  • Containment of who is exposed: synthetic canary users, a feature-flagged cohort, one region, read-only faults before write faults. Degrade reads first; injecting faults into the write path comes later, on higher rungs, with finance's explicit agreement.

The fault menu is wider than "kill the process," and choosing the right one is half the experiment design:

Fault What it actually tests Radius control
Process kill / pod terminate restart, rescheduling, state loss one instance, non-critical
Latency injection (300–1000 ms) timeouts, breakers, SLO-based shedding single upstream, time-boxed
Network packet loss / partition retry budgets, leader election, split-brain one AZ or one node via tc netem
Dependency unavailability circuit breakers, degradation ladder flag-gated to one cohort
CPU / memory stress autoscaling lag, shed thresholds one node, watch for node-level eviction
DNS or clock skew failure failover, lease expiry, retry logic isolated namespace first

Latency injection is usually the highest-value fault and the most underused: real dependencies rarely die cleanly — they get slow, and slow is what kills thread pools. Killing processes tests your restart path; adding 800 ms tests every timeout, breaker, and shedding threshold you have configured.

Tooling is the least interesting part: Chaos Monkey and Netflix's ChAP for scheduled experiments, Gremlin, LitmusChaos or Chaos Mesh for Kubernetes faults, AWS FIS for managed injection, and honestly kubectl cordon/drain, tc netem, and iptables cover a surprising amount. What separates teams is the surrounding process: approval of the window (never during a launch, Black Friday, or alongside a risky deploy — otherwise you cannot attribute symptoms), a shared experiment record, and the discipline that an experiment without a pre-registered hypothesis, an abort condition, and a rollback is just an outage with better branding.

The Game Day: Rehearsing Incident Response With Humans, Not Just Faults

Automated fault injection proves things about your system. A game day proves things about your organization: whether the on-call engineer notices, whether the dashboard answers the question, whether the runbook's third command still exists, whether the status page gets updated before the support inbox fills. Software fails at 02:00 on a Saturday; the people who respond are a component too, and an unattended pod kill does not test them.

A game day has a deliberate arc, and each stage has an artifact:

1. Pre-brief (the day before). Objectives, scenario, injectors, observers, responders, start time, abort criteria, and comms plan. Everyone knows this is an exercise: the fastest way to destroy trust in the program is a "secret" game day where people believe a real incident is unfolding. What must not be pre-announced is the answer — responders get the symptom, not the diagnosis.

2. Injection and observation. The fault goes in at a scheduled minute. Observers timestamp everything: time to detect (does any alert fire?), time to acknowledge (who gets paged, and is it the right person?), time to first correct hypothesis, time to mitigate. The clock on detection is the single most valuable number you will collect, because nothing else in your tooling tells you how long your customers suffered before anyone knew.

3. Response, with the runbook taken literally. Responders execute the documented steps as written — even when a step is obviously wrong, because "step 4 says run ./deploy-failover.sh, which does not exist in any repository" is one of the exercise's findings, and working around it silently in your head defeats the purpose. This stage routinely uncovers dashboards that need three clicks to find, alerts that fire for the wrong service, runbook steps that reference scripts nobody has, an escalation path naming someone who left the company, and rollback credentials nobody on the shift holds.

4. Comms rehearsal. Draft the status-page update and the customer-facing message under time pressure; incident comms written only during real incidents are written badly.

5. Blameless debrief, same day. What did we detect, what did we miss, which runbook steps failed, what was the actual mitigation vs the documented one. "We were lucky" is a finding, not a dismissal.

6. Findings become tickets with owners and dates. This is the step teams skip, and skipping it is what makes the game day theater. Every discovered gap ends up in the backlog, sized, and tracked to done; the next experiment then re-tests that specific claim. Over two or three cycles your resilience documentation stops being aspirational because each entry has been exercised.

What you measure decides whether the program improves: MTTD, MTTR, alert fidelity (right alert, right person, no false-positive storm), runbook step success rate, and time-to-mitigate for each failure mode. Plot them across game days — that trend is your evidence that the program pays for itself.

The two objections share a shape. "We cannot experiment in production" — real traffic shape, data volume, topology, and accumulated config exist only there, so testing elsewhere proves a milder claim; the answer is small blast radius, not abstention. "It's too risky" — unmeasured risk is still risk, and hope without a hypothesis is already an experiment, just an uncontrolled one.

To introduce the practice on a team that has never done it: start in staging with one hypothesis about a protection you already claim to have, small blast radius, one hour, a written log. Then move to a single non-critical instance in production during off-peak. Then schedule quarterly game days with humans in the room. The claims you will be verifying are the ones you built across this whole phase — that your degradation ladder holds (Graceful Degradation and Backpressure), that your breakers trip and recover, that your shed thresholds fire before the OOM killer does — and in an interview, describing an experiment with a hypothesis, a blast radius, an abort criterion, and a follow-up ticket is the most credible way to prove you have actually run one. The same habit — write the claim down, attack it on purpose, track what you found — is exactly what Architecture-Level Threat Modeling formalizes next, where the failures are adversarial instead of accidental.

Quiz

1. Which of these is a valid steady-state hypothesis for a chaos experiment?

Question 1 options

2. You are planning the first production chaos experiment against a service that handles payments. What is the correct scope?

Question 2 options

3. Your injected fault caused real damage - latency tripled and some requests failed - but no alert fired and the dashboards looked normal. What did the experiment show?

Question 3 options

4. During a game day, responders discover that the rollback command in the runbook does not exist in any repository. What is the right outcome?

Question 4 options

Flashcards

Question

What is a steady-state hypothesis and why must it exist before injection?

Answer

A falsifiable statement of normal behavior in numbers (success rate, p99, throughput, queue depth) plus the fault, scope, expectation, abort criteria, and verification. Without it you cannot tell a pass from a blind spot - you are just breaking things, and any result is unfalsifiable.

Question

Define blast radius and list the four controls that bound it in production.

Answer

Blast radius = fraction of real users affected x severity x duration. Controls: an automatic abort wired to steady-state metrics (code, not a person), a hard time box, a rollback command tested beforehand, and containment of exposure via cohort flags, one region, or read-only faults first.

Question

What is the blast-radius escalation ladder and what gates movement between rungs?

Answer

Local/synthetic -> topology-shaped staging -> one non-critical production instance off-peak -> one AZ or tenant cohort -> multi-AZ/region. Each rung is gated by the previous rung’s results being boring; before climbing you confirm shared dependencies (databases, pools) cannot leak the radius.

Question

How does a game day differ from an automated chaos experiment?

Answer

Automated experiments verify system behavior unattended; game days rehearse people and process - detection time, the right page, dashboards, the runbook executed as written, comms, and escalation. Both feed the same log, but only the game day tells you whether humans can recover the system within their MTTR target.

Question

What three outcomes can an experiment produce, and which one teams most often misread?

Answer

Hypothesis holds (evidence), hypothesis fails (a gap found without customers), or real damage with no signal (observability blind). The third is misread as a pass because no alert fired - it is actually the most valuable finding, since customers would have suffered in silence.

Revision Notes

Key Takeaways

  • 1. Steady state in numbers first: a hypothesis with fault, scope, expectation, abort criteria, and verification is what separates an experiment from sabotage
  • 2. Blast radius is users x severity x duration - climb a rung-gated ladder with an automatic metric-wired abort, a time box, and a pre-tested rollback
  • 3. Pick the fault for the claim: latency injection finds timeout and breaker gaps that process kills hide; real dependencies get slow, not dead
  • 4. A game day rehearses detection and response: timestamp detect/acknowledge/mitigate, run the runbook as written, and hold a blameless same-day debrief
  • 5. Findings become tickets with owners and dates, and detection-blindness counts as success - an experiment that proves nothing about your alerts has failed, not passed

Interview Tips

  • • "How would you introduce chaos engineering to a team that has never done it?" - one hypothesis about a protection you already claim, staging with production-shaped topology, small blast radius, a written log, then a single non-critical prod instance off-peak
  • • "How do you keep experiments safe?" - rung-gated escalation, metric-wired automatic abort (not a human), hard time box, tested rollback, cohort containment, and a dependency-graph check for shared databases
  • • "What if nothing breaks?" - say you also verify detection: no alert despite real damage means observability is blind, which is a finding, not a pass
  • • "Chaos in production - isn’t that reckless?" - contrast controlled injection with uncontrolled outages; real topology, traffic, and data volume only exist in prod, so the answer is bounded radius and hypotheses, not hope

Cheat Sheet

Chaos Engineering and Game Days — Cheat Sheet

Hypothesis required before injection: steady-state metric + fault + scope + expectation + abort_if + verification method. Falsifiable numbers ("success ≥ 99.5 %, p99 < 800 ms") — never "make sure it still works."

Three outcomes: hypothesis holds → evidence, not confidence · hypothesis fails → gap found without customers · real damage but no signal → your observability is blind (the most valuable finding of all).

Blast radius ladder (gated rung by rung): local/synthetic → topology-shaped staging → 1 non-critical prod instance off-peak → 1 AZ or tenant cohort → multi-AZ/region. Confirm shared dependencies first — a shared database leaks radius.

Controls: automatic abort wired to steady-state metrics (code, not a person) · hard time box · rollback command tested beforehand · cohort/flag containment · read-only faults before write faults · no experiment during a launch or an unrelated risky deploy.

Fault menu: process kill → restart/reschedule path · latency injection 300–1000 ms → timeouts, breakers, shedding (highest value; real dependencies get slow, not dead) · packet loss/partition (tc netem) → retries, leases, leader election · dependency down → degradation ladder · CPU/memory stress → autoscale lag and shed thresholds.

Game day arc: pre-brief (objectives, roles, abort criteria, comms) → inject on the minute → timestamp detect / acknowledge / hypothesize / mitigate → execute the runbook as written (a missing command is a finding) → comms rehearsal → blameless debrief same day → every finding becomes a ticket with an owner and a date, re-tested next cycle.

Measures across game days: MTTD, MTTR, alert fidelity (right alert, right person, no storm), runbook step success rate, time-to-mitigate per failure mode.

Objections: "can't experiment in prod" → real topology, traffic, and data volume exist only in prod; answer is small radius, not abstention. "Too risky" → untested risk is still risk; hope without a hypothesis is already an experiment, just an uncontrolled one.

Continue Learning