
Chaos Engineering
- 430 installs
- 23.8k repo stars
- Updated July 17, 2026
- alirezarezvani/claude-skills
chaos-engineering is an agent skill that designs bounded fault-injection experiments with steady-state hypotheses, abort criteria, and postmortems for developers who need to prove distributed-system resilience before pro
About
chaos-engineering is a Claude Code agent skill (v2.9.0) from alirezarezvani/claude-skills that turns resilience testing into a disciplined workflow instead of ad hoc outages. It applies Netflix-style chaos principles—steady-state metrics, realistic failures, production-safe scope, and mandatory abort criteria—then generates structured experiment plans through three stdlib-only Python scripts: experiment_designer.py, blast_radius_calculator.py, and experiment_postmortem.py. The skill catalogs seven attack types spanning latency, errors, resource saturation, network partitions, dependency loss, clock skew, and infrastructure kills, with tooling guidance for Chaos Toolkit, Chaos Mesh, Litmus, Gremlin, and AWS FIS. Developers reach for chaos-engineering when planning Game Days, reviewing blast radius against error budgets, or writing experiment postmortems. A bundled /chaos-experiment command walks through target, hypothesis, magnitude, and GREEN/YELLOW/RED risk scoring before any injection runs.
- Hypothesis-driven failure experiments
- Blast-radius and rollback controls
- SLO and latency fault injection
- Dependency and network failure drills
- Runbooks from game-day findings
Chaos Engineering by the numbers
- 430 all-time installs (skills.sh)
- Ranked #280 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/alirezarezvani/claude-skills --skill chaos-engineeringAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 430 |
|---|---|
| repo stars | ★ 23.8k |
| Last updated | July 17, 2026 |
| Repository | alirezarezvani/claude-skills ↗ |
How do you design safe chaos experiments with abort criteria?
Design fault-injection experiments—latency, crashes, dependency loss—to prove resilience and SLOs before production surprises.
Who is it for?
SRE and backend developers running bounded Game Days or fault-injection drills on microservices with defined SLOs and on-call coverage.
Skip if: Developers seeking incident response runbooks, capacity load tests, or red-team penetration exercises without a resilience hypothesis.
When should I use this skill?
User mentions chaos experiment, fault injection, Game Day, blast radius, steady state, Chaos Mesh, Litmus, Gremlin, or AWS FIS.
What you get
Structured chaos experiment plan, blast-radius risk score, rollback checklist, and markdown postmortem with follow-up actions.
- experiment plan markdown
- blast-radius risk score
- postmortem report
By the numbers
- Skill version 2.9.0 with 3 stdlib Python experiment scripts
- Covers 7 fault-injection attack types in the attack taxonomy
- Includes 4 reference docs on chaos principles, design, attacks, and tooling
Files
Chaos Engineering
Design experiments that surface real weaknesses in production systems — without becoming outages. Most "chaos engineering" attempts skip steady-state measurement, define no abort criteria, and have no blast-radius bound. This skill enforces the discipline that makes chaos experiments safe and useful.
When to use
- Planning a chaos experiment (what to break, where, when, how to abort)
- Calculating blast radius before running the experiment
- Reviewing an existing experiment plan for safety
- Choosing a chaos tool (Chaos Toolkit / Chaos Mesh / Litmus / Gremlin / AWS FIS)
- Writing a chaos experiment postmortem
- Running a Game Day exercise
When NOT to use
- General incident response (use
incident-response) - Threat hunting / red-team (use
red-team,threat-detection) - Performance load testing (different goal — chaos is about failure modes, not capacity)
- Production debugging (chaos discovers weaknesses preemptively, not after-the-fact)
Core principle: chaos without abort criteria is an outage
The 4 Principles of Chaos Engineering (Netflix, 2016):
1. Build a hypothesis around steady-state behavior. Not "what breaks?" but "X holds; will it still hold under fault Y?" 2. Vary real-world events. Inject realistic failures: kill nodes, slow networks, lose cache, throttle dependencies. 3. Run experiments in production. Staging never has the same failure modes. Start small. 4. Automate experiments to run continuously. One-off chaos is a press release; continuous chaos is engineering.
Add a fifth: Define abort criteria up front. A chaos experiment with no abort criteria is an outage by another name.
Quick start
SKILL=engineering/chaos-engineering/skills/chaos-engineering
# 1. Design an experiment
python "$SKILL/scripts/experiment_designer.py" --target "checkout-svc" --hypothesis "p99 latency stays <500ms" --attack latency --duration-min 15
# 2. Calculate blast radius
python "$SKILL/scripts/blast_radius_calculator.py" --traffic-share 0.05 --user-pop 1000000 --duration-min 15
# 3. Generate postmortem after the experiment
python "$SKILL/scripts/experiment_postmortem.py" --plan experiment.json --result-log results.txtThe 3 Python tools
All stdlib-only. Run with --help.
experiment_designer.py
Generates a structured experiment plan from inputs. Enforces the required sections (hypothesis, steady-state metric, blast radius, abort criteria, rollback).
python scripts/experiment_designer.py \
--target "checkout-svc" \
--hypothesis "p99 latency stays <500ms when payment-svc is slow" \
--attack latency \
--magnitude "+200ms" \
--duration-min 15 \
--blast-radius "5% of US traffic" \
--abort-if "p99 > 1000ms OR error_rate > baseline + 1pp"Outputs a markdown plan with: hypothesis, steady-state, attack, magnitude, duration, blast radius, abort criteria, rollback procedure, monitoring dashboards, and learning question.
blast_radius_calculator.py
Computes the blast radius of a planned experiment. Given traffic share + user population + duration, calculates expected affected users, expected error budget burn, and a risk score.
python scripts/blast_radius_calculator.py \
--traffic-share 0.05 \
--user-pop 1000000 \
--duration-min 15 \
--baseline-availability 0.999 \
--expected-impact-availability 0.95Outputs:
- Expected affected users
- Error budget consumed (in minutes of error budget)
- Risk score: GREEN / YELLOW / RED
- Recommendation: PROCEED / REDUCE / ABORT
GREEN = <1% error budget; YELLOW = 1-10%; RED = >10%.
experiment_postmortem.py
Produces a structured postmortem from an experiment plan + results. Catches the common postmortem failure modes: no learning recorded, no follow-up actions, blame-laden language.
python scripts/experiment_postmortem.py --plan experiment.json --result-log results.txtOutputs markdown with: summary, hypothesis (was it confirmed/refuted?), what we learned, what surprised us, follow-up actions with owners, and link to next experiment.
The 7 attack types (taxonomy)
Different attacks reveal different weaknesses. See references/attack_taxonomy.md for full detail.
| Attack | What it tests | Tooling |
|---|---|---|
| Latency | Timeouts, retries, circuit breakers | tc, Chaos Mesh NetworkChaos |
| Error | Error handling, fallback paths | Chaos Mesh HTTPChaos, Toxiproxy |
| Resource (CPU, memory, disk) | Saturation handling, autoscaling | Chaos Mesh StressChaos, stress-ng |
| Network partition | Split-brain, consensus, failover | Chaos Mesh NetworkChaos partition |
| Dependency failure | Graceful degradation, fallback | Service mesh fault injection |
| Time | Clock skew, NTP issues | libfaketime, Chaos Mesh TimeChaos |
| Infrastructure (kill instance) | Auto-recovery, failover | AWS FIS, Chaos Monkey |
Pick the attack that matches the hypothesis. "What happens if X is slow?" → latency. "What happens if X loses network?" → partition.
Tooling chooser
| Tool | Best for | Pricing | Stack |
|---|---|---|---|
| Chaos Toolkit | Lightweight, language-agnostic, JSON experiments | OSS | Any |
| Chaos Mesh | Kubernetes-native, rich CRDs, in-cluster | OSS | Kubernetes |
| Litmus | Kubernetes, Argo-integrated, large library | OSS + Enterprise | Kubernetes |
| Gremlin | Enterprise SaaS, multi-cloud, audit | Paid | Any |
| AWS FIS | AWS-native, IAM-integrated, EC2/ECS/EKS | Paid (AWS) | AWS |
| Custom | Niche needs, single-cloud, low budget | None | Any |
Decision rules:
- k8s-only stack + OSS → Chaos Mesh or Litmus (Litmus has bigger experiment library)
- Multi-cloud + OSS → Chaos Toolkit
- AWS-heavy + simple needs → AWS FIS
- Enterprise + audit/compliance → Gremlin
See references/tooling_landscape.md for trade-offs.
Workflows
Workflow 1: Design and run a single experiment
1. State a hypothesis: "When [fault], steady-state metric X stays within Y."
2. Identify the steady-state metric — must be measurable BEFORE the experiment.
3. Run blast_radius_calculator.py — confirm GREEN before proceeding.
4. Run experiment_designer.py to produce the plan.
5. Get a peer review of the plan; confirm abort criteria are concrete.
6. Notify the on-call team in #incidents (or whatever channel).
7. Run the experiment with monitoring open.
8. If abort criteria are hit, abort immediately; record what happened.
9. Run experiment_postmortem.py to capture learnings.
10. File follow-up actions; link to next experiment.Workflow 2: Game Day exercise
1. Pick a scenario (e.g., "primary database fails over").
2. Identify all dependent services that should keep working.
3. Build a multi-experiment plan covering each layer.
4. Schedule with stakeholders; on-call coverage required.
5. Run with a facilitator who manages the scenario.
6. Capture observations in a shared doc as they happen.
7. Single combined postmortem covering all observations.
8. Track follow-up actions in a board with owners.Workflow 3: Continuous chaos (game days → daily)
1. Start: weekly Game Day in staging.
2. Move to: weekly Game Day in production with limited blast radius.
3. Mature to: continuous chaos via scheduled experiments (Litmus chaos schedule, Gremlin scenarios).
4. Wire to deployment: every prod deploy triggers a baseline chaos sweep.
5. Track: experiments per week, weaknesses discovered, MTTR trend.Composition with other skills
This skill explicitly composes with two others in this library:
| Skill | Composition |
|---|---|
feature-flags-architect | Kill switches defined there are the abort triggers here |
kubernetes-operator | Operators are common chaos targets (test reconcile under fault) |
incident-response | Chaos experiments that escalate become incidents |
Anti-patterns
- No hypothesis — "let's break things" is sabotage, not engineering
- No steady-state metric — without a baseline, you can't tell if X broke
- No blast radius bound — full-prod experiment without limits = outage
- No abort criteria — see above; this is mandatory
- No on-call coverage — chaos without monitoring is unmonitored production
- Chaos in staging only — staging never has prod failure modes
- Chaos in dev — useless; dev has different failure modes from prod
- One-off chaos — single experiment is a press release; learning requires recurrence
- Blame-laden postmortem — record causes, not blame; teams stop running chaos otherwise
References
references/chaos_principles.md— the 4 principles, history, when to startreferences/experiment_design.md— hypothesis structure, steady-state metrics, abort criteriareferences/attack_taxonomy.md— 7 attack types with examples and toolingreferences/tooling_landscape.md— Chaos Toolkit / Mesh / Litmus / Gremlin / FIS / DIY
Slash command
/chaos-experiment — interactive experiment design wizard that runs all 3 tools.
Asset templates
assets/experiment_template.md— fill-in plan templateassets/postmortem_template.md— structured postmortem template
Verifiable success
A team using this skill should achieve:
- 100% of chaos experiments have a written hypothesis, abort criteria, and blast-radius calculation
- Blast radius for any single experiment never exceeds 10% of error budget
- Mean time between chaos experiments <14 days (continuous, not one-off)
- Each experiment produces ≥1 follow-up action that gets shipped
- No chaos experiment escalates to a customer-impacting incident in trailing 90 days
Chaos Experiment
Fill in every section before running. Refuse to run if any section is empty.
Identity
- Experiment ID:
<auto-generated; format: chaos-<target>-<attack>-<unix-ts>> - Date:
<YYYY-MM-DD> - Owner:
<your-handle@team> - On-call team:
<team channel / pager> - Reviewer:
<peer who reviewed this plan>
1. Hypothesis
When<fault>,<steady-state metric>stays<tolerance>.
Example: When payment-svc is +200ms slow, checkout p99 stays below 500ms.
2. Steady-state metric
- Metric:
<e.g., p99 checkout latency> - Baseline window:
<e.g., 5 minutes pre-experiment> - Tolerance:
<e.g., within ±5% of baseline> - Dashboard:
<URL>
3. Attack
- Type:
[ ] latency [ ] error [ ] cpu [ ] memory [ ] disk [ ] network-partition [ ] dependency-failure [ ] time-skew [ ] kill-instance - Magnitude:
<e.g., +200ms> - Duration:
<minutes> - Target:
<service / pod / instance / region> - Tooling:
<Chaos Toolkit / Chaos Mesh / Litmus / Gremlin / AWS FIS / Custom>
4. Blast radius
- Traffic share:
<e.g., 5% of US> - Expected affected users:
<from blast_radius_calculator.py> - Error budget consumed:
<from blast_radius_calculator.py> - Risk score:
[ ] GREEN [ ] YELLOW [ ] RED
5. Abort criteria
Auto-trigger experiment termination if ANY of these hit.
- [ ]
<signal 1, e.g., p99 > 1000ms> - [ ]
<signal 2, e.g., 5xx rate > baseline + 1pp> - [ ]
<signal 3, e.g., on-call paged SEV1/SEV2>
6. Rollback procedure
1. <step to disable fault, e.g., "kubectl delete chaos networkchaos/<name>"> 2. Verify steady state recovers within 2 minutes 3. If not recovering, escalate as incident; restore from backup if needed
7. Learning question
What do you expect NOT to learn? Force yourself to predict.
<your prediction>
Pre-flight checklist
- [ ] Hypothesis written
- [ ] Steady-state metric measured for ≥5 min
- [ ] Blast radius calculated (GREEN or YELLOW only)
- [ ] Abort criteria documented with thresholds
- [ ] Rollback procedure tested in staging
- [ ] On-call team notified
- [ ] Monitoring dashboards open
- [ ] Owner identified and reachable
- [ ] Time-box agreed
- [ ] Communication plan if abort triggers
Post-experiment
Run experiment_postmortem.py --plan <plan.json> --result-log <results> to generate the postmortem.
Chaos Experiment Postmortem
Identity
- Experiment:
<experiment_id> - Date:
<YYYY-MM-DD> - Target:
<service> - Owner:
<handle@team> - Postmortem facilitator:
<handle@team>
Hypothesis
<hypothesis from the plan>Outcome
- [ ] Held — hypothesis confirmed
- [ ] Refuted — hypothesis disproven
- [ ] Inconclusive — could not tell
Timeline
| Time | Event |
|---|---|
| T-5min | Started baseline measurement |
| T+0 | Attack injected |
| T+? | <observation> |
| T+? | <observation> |
| T+N | Attack ended (or aborted) |
| T+N+2 | Steady state recovered |
What we learned
<at least one concrete learning — required>
What surprised us
<unexpected observations; "nothing surprised us" is a signal that you didn't push hard enough>
What failed
<things that broke during the experiment that shouldn't have>
What held
<things that worked as expected — confidence-building data points>
Root causes (if any failures)
<technical analysis without blame>
Follow-up actions
| Action | Owner | Due | Status |
|---|---|---|---|
<concrete action> | <@owner> | <date> | [ ] |
<concrete action> | <@owner> | <date> | [ ] |
Every experiment should produce ≥1 follow-up. If none — re-examine whether you tested anything new.
Next experiment
<what's the next experiment that builds on this learning?>
Stakeholder summary (1-2 sentences)
<for the team channel; describe outcome and biggest learning>
Attack taxonomy
7 categories of fault injection. Each tests a different system property. Pick the one whose failure mode matches your hypothesis.
1. Latency
What it tests: timeouts, retries, circuit breakers, fallback paths.
Inject: add N ms of delay to network responses to a target.
When to use:
- "What if dependency X is slow?"
- "Are timeouts configured correctly upstream?"
- "Does the retry budget kick in?"
Tools:
- Linux
tc(traffic control) — direct kernel-level shaping - Chaos Mesh
NetworkChaos(delay) - Toxiproxy — proxy-based, language-agnostic
- AWS FIS —
aws:network:traffic-controlaction
Example magnitude: +200ms (90% of typical timeouts), +2000ms (test backoff), +30s (test giving-up logic).
2. Error injection
What it tests: error handling paths, fallback behavior, retry policies.
Inject: return errors (5xx, exceptions) for a fraction of requests.
When to use:
- "What happens when X starts failing?"
- "Does the fallback path actually work in prod?"
- "Are we logging errors correctly?"
Tools:
- Chaos Mesh
HTTPChaos - Service mesh (Istio, Linkerd) fault injection
- Toxiproxy with error toxic
- Application-level feature flag for synthetic errors
Example magnitude: 1% errors (test handler), 50% errors (test retry), 100% errors (test fallback path).
3. Resource exhaustion
What it tests: saturation handling, autoscaling, OOM behavior, disk-full handling.
Inject: consume CPU, memory, or disk on the target.
When to use:
- "What if memory leaks?"
- "Does the autoscaler kick in?"
- "What happens when disk fills?"
Sub-types:
- CPU pressure — peg cores at N% usage
- Memory pressure — allocate large blocks
- Disk fill — write large files until partition fills
- I/O saturation — high random read/write
Tools:
stress-ng— CPU/memory/IO/disk- Chaos Mesh
StressChaosandIOChaos - AWS FIS
aws:ssm:send-commandwith stress-ng
Example magnitude: 80% CPU sustained, 90% memory, fill /var to 95%.
4. Network partition
What it tests: consensus protocols, leader election, split-brain prevention, region failover.
Inject: drop all packets between a set of hosts.
When to use:
- "What if AZ-A loses connectivity to AZ-B?"
- "Does the database elect a new primary?"
- "Does the cluster avoid split-brain?"
Tools:
- Chaos Mesh
NetworkChaos(partition mode) tcwith iptables drop rules- AWS FIS
aws:network:disrupt-connectivity
Example magnitude: drop 100% to peer X (full partition), drop 50% (degraded link).
5. Dependency failure
What it tests: graceful degradation, fallback to cache, fallback to default values.
Inject: make a downstream dependency unavailable (timeout, refuse connections).
When to use:
- "What if the rec engine goes down?"
- "Does Search degrade gracefully when ML models are unreachable?"
- "Is cache the fallback for the user-pref service?"
Tools:
- Service mesh fault injection (most flexible)
- Toxiproxy
- iptables rules to refuse connections
- Chaos Mesh
NetworkChaoswithcorruptordrop
Example magnitude: 100% requests to dep X timeout (full outage), 25% timeout (intermittent), 0% available for 5 min (sustained outage).
6. Time skew
What it tests: time-sensitive logic — token expiry, cron schedules, TTLs, retry backoff.
Inject: alter the wall clock seen by a process.
When to use:
- "What if NTP fails?"
- "What if a process clock drifts +5 minutes?"
- "Do tokens correctly fail validation when expired?"
- "Does cron skip or double-fire?"
Tools:
libfaketime— preload library- Chaos Mesh
TimeChaos - Custom: change container's
/etc/localtime
Example magnitude: +1 minute (subtle), +5 minutes (TLS / token failures), +1 day (catastrophic for some logic).
Caution: time skew can cause cluster-wide consensus failures. Test in isolation first.
7. Infrastructure (kill instance / pod / container)
What it tests: auto-recovery, failover, replica count maintenance.
Inject: terminate an instance, pod, or container.
When to use:
- "Does Kubernetes restart the pod?"
- "Does the load balancer remove the instance from rotation?"
- "Is the replication factor maintained?"
Tools:
- Chaos Monkey (the original)
- Chaos Mesh
PodChaos(kill, fail) - AWS FIS
aws:ec2:terminate-instances kubectl delete pod(manual, simplest)
Example magnitude: kill 1 of N pods (Chaos Monkey level), kill all pods of a deployment (test recreation), kill 1 of 3 replica DB nodes (test failover).
Choosing an attack
| Hypothesis pattern | Attack type |
|---|---|
| "What if X is slow?" | Latency |
| "What if X is failing?" | Error |
| "What if we run hot?" | Resource |
| "What if regions partition?" | Network partition |
| "What if dep X is down?" | Dependency failure |
| "What if clocks drift?" | Time skew |
| "What if a node dies?" | Infrastructure |
Combining attacks
Real outages often combine attacks (e.g., latency + saturation). Once basic experiments are stable, run combinations:
- Latency on dependency + CPU pressure on app → tests timeout + retry budget interaction
- Pod kill + network partition → tests recovery during a partition
- Disk fill + dependency failure → tests fallback path while disk is constrained
Combinations have higher risk; reduce blast radius accordingly.
Severity ladder
S1 — Latency (small) ← start here
S2 — Error injection (low %)
S3 — Resource pressure (CPU/mem)
S4 — Latency (large) / errors (high %)
S5 — Single instance kill
S6 — Network partition (single peer)
S7 — Multiple instance kill
S8 — Region partition / time skew
S9 — Combinations of S5-S8 ← here be dragonsDon't skip levels. Earn confidence at S1-S3 before attempting S5+.
The principles of chaos engineering
Chaos engineering is the discipline of experimenting on a system in order to build confidence in its capability to withstand turbulent conditions in production. The phrase comes from Netflix's 2014-2016 work productizing what started as Chaos Monkey.
The 4 founding principles (Netflix, 2016)
1. Build a hypothesis around steady-state behavior
Steady state = a measurable, normal-operations metric (latency, throughput, conversion rate, error rate).
Bad: "What happens if the database goes down?" Good: "When the primary database fails over, p99 checkout latency stays below 800ms and conversion rate stays within 2% of baseline."
The hypothesis must be falsifiable — there must be a measurement that can disprove it.
2. Vary real-world events
Inject realistic failure modes:
- Servers crash
- Networks partition or slow
- Disks fill
- Dependencies time out or return errors
- Caches lose data
- Time skews
Don't inject implausible events (e.g., "what if all 50 zones in 5 regions go down simultaneously"). That's not chaos engineering, that's astronomy.
3. Run experiments in production
Staging never reproduces:
- Real traffic patterns
- Real cache hit rates
- Real cross-service dependencies
- Real data volumes
- Real user behavior
The only system that has prod failure modes is prod. Start with tiny blast radius (1%), grow as confidence grows.
4. Automate experiments to run continuously
A single chaos experiment is a press release. Continuous chaos is engineering.
Maturity progression: 1. Manual one-offs → 2. Weekly Game Days → 3. Scheduled experiments → 4. Continuous chaos in CI/CD
The 5th principle this skill adds:
5. Define abort criteria up front
A chaos experiment with no abort criteria is an outage. Every plan must include:
- A specific signal (metric, threshold)
- A specific action (auto-abort, manual abort, escalate)
- A timeline (within N seconds of breach)
If the threshold is hit, abort immediately. Investigate later.
When to start
You're ready for chaos engineering when:
- [ ] You have basic monitoring (you can detect a steady-state breach)
- [ ] You have on-call rotations (someone is watching when chaos runs)
- [ ] You have at least one tool to inject the desired fault
- [ ] You have an SLO/SLI defined (so you know what "good" looks like)
- [ ] You have postmortem culture that's blameless
- [ ] You have a leadership champion who'll defend the practice
If any of these are missing, fix them first. Premature chaos = outages with no learning.
When NOT to do chaos engineering
- During a release freeze
- During a known incident
- During peak traffic events without explicit approval
- On systems that don't have steady-state metrics
- On systems where you can't bound the blast radius
- On the day of a security disclosure
- When the team is already firefighting
Maturity model
| Level | Description | Cadence | Tooling |
|---|---|---|---|
| L0 | None | n/a | none |
| L1 | Manual one-offs in staging | quarterly | tc, manual scripts |
| L2 | Weekly Game Days in staging | weekly | Chaos Toolkit, internal scripts |
| L3 | Limited prod experiments | weekly | Chaos Toolkit / Mesh / Litmus / FIS |
| L4 | Continuous prod chaos with bounded blast radius | daily | Chaos Mesh / Gremlin scenarios |
| L5 | Chaos in CI/CD pipeline; deploys auto-trigger sweeps | per-deploy | Custom + tooling stack |
Most teams should target L3 within 6-12 months of starting. L5 is rare and only justified for the largest distributed systems.
Common objections (and counters)
| Objection | Counter |
|---|---|
| "We can't break production!" | You already do, just unintentionally. Chaos is intentional, bounded, observed breaks. |
| "This is a customer-facing system." | Start at 1% blast radius. The 99% are unaffected. |
| "We don't have time." | Chaos finds bugs that would otherwise become 4am pages. Time spent on chaos saves time on incidents. |
| "Our system is too critical." | Critical systems have the most to gain from learning their failure modes. |
| "We have HA already." | HA without chaos is HA in theory. Chaos finds gaps in actual HA. |
What a steady-state metric looks like
Good steady-state metrics:
- p99 request latency (objective, measurable per second)
- Error rate (objective, measurable)
- Conversion rate (business metric, slow but real)
- Successful logins per minute (business + tech signal)
- Queue depth (system health)
Bad metrics:
- "Things feel slow" (not measurable)
- CPU usage (a means, not an end)
- Number of pods running (not customer-facing)
Pick metrics that customers feel. CPU can spike without customer impact; latency and errors can't.
History
- 2010: Netflix launches Chaos Monkey (kills random EC2 instances)
- 2011: Simian Army expands (Latency Monkey, Conformity Monkey, etc.)
- 2014: Chaos engineering term coined; principles drafted
- 2016: principlesofchaos.org published
- 2018: Chaos Toolkit released as OSS
- 2019: Chaos Mesh and Litmus mature for Kubernetes
- 2020: AWS launches Fault Injection Simulator (FIS)
- 2023+: Chaos engineering becomes mainstream practice in SRE-heavy orgs
Further reading
- principlesofchaos.org — the foundational document
- Chaos Engineering (Casey Rosenthal, Nora Jones) — O'Reilly, 2020
- Learning Chaos Engineering (Russ Miles) — O'Reilly, 2019
- Netflix Tech Blog on Chaos Engineering posts (2016-2020)
Experiment design
A well-designed chaos experiment has 7 sections. Skip any of them and the experiment becomes either useless (no learning) or dangerous (no bounds).
The 7 sections
1. Hypothesis
2. Steady-state metric
3. Attack
4. Blast radius
5. Abort criteria
6. Rollback procedure
7. Learning question1. Hypothesis
Format: When [fault], [steady-state metric] stays [tolerance].
Examples:
- "When the primary Postgres replica fails, checkout p99 latency stays below 500ms."
- "When 50% of payment-service requests are throttled to 1 RPS, conversion rate drops by less than 5% within 60 seconds of return-to-normal."
- "When us-east-1 is partitioned from us-west-2, Search continues to return results from us-west-2 within 200ms p99."
A good hypothesis:
- Names a specific fault (not "things break")
- Names a specific metric (not "everything")
- States a specific tolerance (not "good enough")
- Is measurable and falsifiable
2. Steady-state metric
The metric you'll measure before, during, and after the experiment.
Required properties:
- Quantitative — a number, not a feeling
- Customer-relevant — something users feel (latency, error rate, conversion)
- Measurable in <60s — slow metrics give you no time to abort
- Stable in normal operation — you need a baseline
| Good | Bad |
|---|---|
| p99 checkout latency | "the system is healthy" |
| 4xx + 5xx rate | "errors are low" |
| Successful login rate | CPU usage |
| Items added to cart per minute | replica count |
3. Attack
The fault you're injecting. Must specify:
- Type — latency, error, resource, partition, dependency, time, infrastructure
- Magnitude — how much (e.g., "+200ms", "10% errors", "100% timeout to peer X")
- Duration — how long the attack runs (typically 5-30 minutes)
- Target — which subset of the system gets the attack
See attack_taxonomy.md for the 7 attack types.
4. Blast radius
The maximum scope of customer impact. Use blast_radius_calculator.py to compute:
- Affected users —
traffic_share × user_population - Error budget consumed —
duration × traffic_share × availability_delta - Risk score — GREEN (<1% budget) / YELLOW (1-10%) / RED (>10%)
Rule of thumb:
- Start at 1% traffic share
- Grow only after 3 successful experiments at the previous level
- Never exceed 10% of monthly error budget in a single experiment
5. Abort criteria
The signals that auto-trigger experiment termination. Each must be:
- Concrete — specific metric and threshold ("p99 > 1000ms" not "performance degrades")
- Detectable in <60s — latency, error rate, throughput
- Wired to action — manual abort link in the dashboard, automatic via alert webhook
Standard abort criteria:
| Signal | Threshold | Action |
|---|---|---|
| p99 latency | > 2× baseline | abort |
| 5xx rate | > baseline + 1pp | abort |
| 4xx rate (excl. 401/404) | > baseline + 5pp | abort |
| Conversion rate | < baseline × 0.95 | abort |
| Customer ticket spike | > 3× baseline | escalate |
| On-call paged | any SEV1/SEV2 | abort |
6. Rollback procedure
How you'll revert the fault. Required because:
- Sometimes the chaos tool itself fails to revert
- Sometimes the fault has lingering effects (caches, connections)
Standard rollback: 1. Disable fault injection in tool 2. Verify steady-state recovers within 2 minutes 3. If not recovering, escalate as incident; restore from backup if needed
7. Learning question
What do you expect NOT to learn? Force yourself to predict the outcome.
Examples:
- "We expect the cache to absorb the latency. We'll learn whether the timeout configuration on the upstream is correct."
- "We expect failover to take 30s. We'll learn whether retry backoff is configured."
If you predicted the outcome correctly: confidence increased. If you didn't: there's an unknown — file a follow-up.
Pre-flight checklist
Before running the experiment, verify:
- [ ] Hypothesis written
- [ ] Steady-state metric measured for ≥5 minutes
- [ ] Blast radius calculated (GREEN or YELLOW)
- [ ] Abort criteria documented with thresholds
- [ ] Rollback procedure tested in staging
- [ ] On-call team notified in the team channel
- [ ] Monitoring dashboards open
- [ ] Owner identified and reachable
- [ ] Time-box agreed (max experiment duration)
- [ ] Communication plan if abort triggers
Time-boxing
| Experiment type | Typical duration | Max recommended |
|---|---|---|
| First-time chaos | 5 minutes | 10 minutes |
| Familiar attack, new target | 15 minutes | 30 minutes |
| Continuous (automated) | per scheduler | 10 min per attack |
| Game Day (human-led) | 1-2 hours | 4 hours |
Escalation
If abort criteria are hit:
1. Stop the experiment immediately (the obvious step many teams forget to script) 2. Verify steady-state recovery 3. If recovery doesn't happen in 5 min → declare an incident 4. Open a postmortem doc using experiment_postmortem.py 5. Notify stakeholders (whoever was promised "this won't impact anything") 6. Capture timeline while memory is fresh
Anti-patterns
- Hypothesis written after running — that's a postmortem, not chaos engineering
- Steady-state metric chosen during experiment — pick before
- Magnitude "small" — quantify; "small" varies by reader
- No abort criteria — never run without them
- Single owner of all chaos — culture problem; spread the practice
- Chaos that always succeeds — increase magnitude; you're not learning if everything passes
- Chaos that always fails — reduce magnitude; you can't learn if everything breaks
- Chaos with no follow-up actions — what was the point?
Tooling landscape
Six options. Pick by stack, license preference, and required attack types.
At-a-glance
| Tool | License | Stack | Attack coverage | Best for |
|---|---|---|---|---|
| Chaos Toolkit | OSS (Apache 2) | Any (Python) | Broad via plugins | Lightweight, multi-cloud, JSON experiments |
| Chaos Mesh | OSS (Apache 2) | Kubernetes | Very broad (network, pod, IO, time, stress) | k8s-native, rich CRDs |
| Litmus | OSS (Apache 2) | Kubernetes | Very broad (300+ experiments) | k8s, Argo-integrated |
| Gremlin | Commercial | Any (agents) | Broad, polished | Enterprise, audit, multi-cloud |
| AWS FIS | Paid (AWS) | AWS | AWS services + EC2/ECS/EKS | AWS-heavy, IAM-integrated |
| Custom | Your code | Any | What you build | Niche, single-cloud, low budget |
Decision tree
Stack constraint?
├── Kubernetes-only ──┬── OSS preferred → Chaos Mesh OR Litmus
│ │ (Litmus has the bigger experiment library;
│ │ Chaos Mesh has cleaner CRD model)
│ └── Enterprise budget → Gremlin
│
├── AWS-heavy ────────┬── Simple needs → AWS FIS
│ ├── Multi-cloud + AWS → Chaos Toolkit + AWS plugin
│ └── Enterprise → Gremlin
│
├── Multi-cloud ──────┬── OSS → Chaos Toolkit
│ └── Enterprise → Gremlin
│
└── No infra constraint
└── Just need fault injection → Toxiproxy (a single-purpose tool, not full chaos framework)Chaos Toolkit
What it is: Python-based framework. You write experiments as JSON or YAML files; the CLI runs them.
Strengths:
- Lightweight; runs anywhere Python runs
- Plugin ecosystem for AWS, Azure, GCP, Kubernetes, etc.
- JSON experiments are version-controllable
- Apache 2 license
Weaknesses:
- No built-in scheduling (you bring cron / CI)
- Smaller experiment library than Litmus
- Plugin quality varies
Example experiment (JSON):
{
"title": "Latency on payment-svc",
"description": "p99 latency stays <500ms when payment is +200ms slow",
"steady-state-hypothesis": {
"title": "p99 < 500ms",
"probes": [{ "type": "probe", "tolerance": [0, 500],
"provider": { "type": "http", "url": "https://my.dashboards/p99" } }]
},
"method": [{ "type": "action", "name": "add-latency",
"provider": { "type": "process", "path": "tc", "arguments": [...] } }]
}Chaos Mesh
What it is: Kubernetes operator + CRDs for chaos. Install in-cluster; kubectl apply an experiment.
Strengths:
- True k8s-native (no external orchestrator)
- Comprehensive coverage: network, pod, IO, stress, time, DNS, HTTP, kernel
- UI dashboard for running experiments
- CNCF Incubating project
Weaknesses:
- k8s-only
- CRD layout is opinionated; some types feel similar but aren't
- Setup requires cluster admin
Example experiment (CRD):
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: payment-latency
spec:
action: delay
mode: one
selector:
namespaces: [default]
labelSelectors:
app: payment-svc
delay:
latency: 200ms
duration: 5mLitmus
What it is: Kubernetes chaos framework with a large experiment library. Argo-CD integration.
Strengths:
- 300+ pre-built experiments
- Strong Argo / GitOps integration
- ChaosHub community library
- Workflow capability for multi-step experiments
Weaknesses:
- More moving parts than Chaos Mesh
- Some pre-built experiments are thin wrappers; quality varies
- k8s-only
Gremlin
What it is: Commercial SaaS. Agents on hosts; central control plane.
Strengths:
- Polished UX
- Comprehensive attack library
- Audit logs (compliance)
- Multi-cloud, multi-OS
- Customer support
Weaknesses:
- Paid (per-host or per-MAU)
- Vendor lock-in
- Less control than OSS
When to choose: large enterprise, compliance/audit requirements, dedicated chaos team, budget exists.
AWS FIS (Fault Injection Simulator)
What it is: AWS-managed chaos service. Templates of "actions" (stop instance, throttle API) chained into experiments.
Strengths:
- IAM-integrated (proper auth/audit)
- Native to AWS services (RDS failover, ECS/EKS, Network Manager)
- Pay-per-experiment (no agents to maintain)
Weaknesses:
- AWS-only
- Smaller attack library than Chaos Mesh / Gremlin
- Multi-account is awkward
When to choose: AWS-heavy team that wants chaos without managing the chaos infra.
Custom (DIY)
When to choose:
- Single-cloud, single-stack, low complexity
- Budget = $0
- Have engineering capacity to maintain the tool
- Need a niche attack type that no tool covers
Implementation patterns:
- Bash scripts that wrap
tc/ iptables / kill / stress-ng - Application-level chaos via feature flags + middleware
- Service mesh fault injection (Istio / Linkerd) — covers many cases without a chaos framework
Trade-offs:
- You build all the safety rails (abort, timeout, blast-radius)
- You build the scheduler
- You debug your own bugs
For most teams, this is a starter path; once chaos becomes regular, switch to a real tool.
Pricing rule of thumb
| Tool | Typical cost (annual) |
|---|---|
| Chaos Toolkit | $0 |
| Chaos Mesh | $0 |
| Litmus OSS | $0 |
| Litmus Enterprise | $5-30k |
| Gremlin | $20-100k+ |
| AWS FIS | pay-per-action, ~$100-2000/mo for active use |
| Custom | engineering time only |
Migration paths
| From | To | Effort |
|---|---|---|
| Custom scripts | Chaos Toolkit | Low (wrap scripts as actions) |
| Chaos Toolkit | Chaos Mesh | Medium (k8s-only; rewrite for CRDs) |
| Chaos Mesh | Litmus | Medium (similar shape, different CRDs) |
| Anything | Gremlin | Easy (Gremlin imports many formats) |
Selection checklist
Before committing:
- [ ] Stack matches (k8s vs multi-cloud vs AWS-only)
- [ ] Required attack types covered (cross-reference
attack_taxonomy.md) - [ ] Audit logging requirement met (Gremlin / AWS FIS only have full audit)
- [ ] Self-hosting requirement met (OSS only)
- [ ] Budget approved
- [ ] Run a 30-day proof-of-concept; verify abort path works
#!/usr/bin/env python3
"""Compute blast radius and risk score for a chaos experiment.
Inputs: traffic share affected, user population, duration, baseline availability,
expected impacted availability. Outputs expected affected users, error budget
consumed, and a GREEN / YELLOW / RED risk score with PROCEED / REDUCE / ABORT
recommendation.
"""
import argparse
import json
import sys
def calculate(traffic_share, user_pop, duration_min, baseline_avail, impacted_avail, monthly_budget_min):
if not 0 <= traffic_share <= 1:
raise ValueError("traffic-share must be between 0 and 1")
if not 0 < impacted_avail <= 1:
raise ValueError("impacted-availability must be between 0 (exclusive) and 1")
if not 0 < baseline_avail <= 1:
raise ValueError("baseline-availability must be between 0 (exclusive) and 1")
affected_users = int(user_pop * traffic_share)
delta_avail = max(baseline_avail - impacted_avail, 0.0)
error_budget_consumed_min = round(duration_min * traffic_share * delta_avail, 4)
pct_of_monthly_budget = round(100 * error_budget_consumed_min / monthly_budget_min, 2) if monthly_budget_min > 0 else 0
if pct_of_monthly_budget < 1:
risk = "GREEN"
recommendation = "PROCEED"
elif pct_of_monthly_budget < 10:
risk = "YELLOW"
recommendation = "PROCEED with explicit owner sign-off; consider reducing traffic share"
else:
risk = "RED"
recommendation = "ABORT or REDUCE — blast radius exceeds 10% of monthly error budget"
return {
"inputs": {
"traffic_share": traffic_share,
"user_pop": user_pop,
"duration_min": duration_min,
"baseline_availability": baseline_avail,
"impacted_availability": impacted_avail,
"monthly_budget_min": monthly_budget_min,
},
"expected_affected_users": affected_users,
"expected_availability_delta": round(delta_avail, 4),
"error_budget_consumed_min": error_budget_consumed_min,
"pct_of_monthly_budget": pct_of_monthly_budget,
"risk": risk,
"recommendation": recommendation,
}
def render_text(result):
print("Blast Radius Calculator")
print("=" * 40)
i = result["inputs"]
print(f"Traffic share affected: {i['traffic_share'] * 100:.2f}%")
print(f"User population: {i['user_pop']:,}")
print(f"Duration: {i['duration_min']} min")
print(f"Baseline availability: {i['baseline_availability']}")
print(f"Impacted availability: {i['impacted_availability']}")
print(f"Monthly error budget: {i['monthly_budget_min']} min")
print("")
print(f"Expected affected users: {result['expected_affected_users']:,}")
print(f"Availability delta: {result['expected_availability_delta']}")
print(f"Error budget consumed: {result['error_budget_consumed_min']} min ({result['pct_of_monthly_budget']}% of monthly)")
print("")
print(f"Risk: {result['risk']}")
print(f"Recommendation: {result['recommendation']}")
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--traffic-share", type=float, help="Fraction (0-1) of traffic affected")
ap.add_argument("--user-pop", type=int, help="Total user population")
ap.add_argument("--duration-min", type=int, help="Experiment duration in minutes")
ap.add_argument("--sample", action="store_true",
help="Run with embedded sample inputs (5%% traffic, 100k users, 30 min)")
ap.add_argument("--baseline-availability", type=float, default=0.999, help="Baseline availability (default: 0.999)")
ap.add_argument("--expected-impact-availability", type=float, default=0.95, dest="impact_avail",
help="Availability under fault (default: 0.95)")
ap.add_argument("--monthly-budget-min", type=float, default=43.2,
help="Monthly error budget in minutes (default: 43.2 for 99.9%% on 30 days)")
ap.add_argument("--format", choices=["text", "json"], default="text")
args = ap.parse_args()
if args.sample:
traffic_share, user_pop, duration_min = 0.05, 100000, 30
elif None not in (args.traffic_share, args.user_pop, args.duration_min):
traffic_share, user_pop, duration_min = args.traffic_share, args.user_pop, args.duration_min
else:
ap.error("--traffic-share, --user-pop and --duration-min are required (or use --sample)")
try:
result = calculate(
traffic_share, user_pop, duration_min,
args.baseline_availability, args.impact_avail, args.monthly_budget_min,
)
except ValueError as e:
print(f"ERROR: {e}", file=sys.stderr)
return 2
if args.format == "json":
print(json.dumps(result, indent=2))
else:
render_text(result)
return 0 if result["risk"] != "RED" else 1
if __name__ == "__main__":
sys.exit(main())
#!/usr/bin/env python3
"""Generate a structured chaos engineering experiment plan.
Enforces the required sections (hypothesis, steady-state metric, blast radius,
abort criteria, rollback). Output is markdown by default; JSON available for
piping into experiment_postmortem.py.
"""
import argparse
import json
import sys
from datetime import datetime, timezone
ATTACK_DEFAULTS = {
"latency": {"magnitude_hint": "+200ms", "tooling_hint": "tc / Chaos Mesh NetworkChaos"},
"error": {"magnitude_hint": "10% of requests return 5xx", "tooling_hint": "Toxiproxy / Chaos Mesh HTTPChaos"},
"cpu": {"magnitude_hint": "80% sustained", "tooling_hint": "stress-ng / Chaos Mesh StressChaos"},
"memory": {"magnitude_hint": "+1GiB pressure", "tooling_hint": "stress-ng / Chaos Mesh StressChaos"},
"disk": {"magnitude_hint": "fill /var to 95%", "tooling_hint": "stress-ng / Chaos Mesh IOChaos"},
"network-partition": {"magnitude_hint": "drop 100% to peer X", "tooling_hint": "Chaos Mesh NetworkChaos partition"},
"dependency-failure": {"magnitude_hint": "100% timeout to dependency", "tooling_hint": "service mesh fault injection"},
"time-skew": {"magnitude_hint": "+5 minutes", "tooling_hint": "libfaketime / Chaos Mesh TimeChaos"},
"kill-instance": {"magnitude_hint": "1 of N instances", "tooling_hint": "AWS FIS / Chaos Monkey"},
}
def build_plan(args):
attack_meta = ATTACK_DEFAULTS.get(args.attack, {})
magnitude = args.magnitude or attack_meta.get("magnitude_hint", "<set magnitude>")
tooling = args.tooling or attack_meta.get("tooling_hint", "<set tooling>")
plan = {
"experiment_id": f"chaos-{args.target}-{args.attack}-{int(datetime.now(timezone.utc).timestamp())}",
"created": datetime.now(timezone.utc).isoformat(),
"target": args.target,
"hypothesis": args.hypothesis,
"steady_state": {
"metric": args.steady_metric or "<must define before experiment>",
"baseline_window": "5 minutes pre-experiment",
"tolerance": args.tolerance or "within ±5% of baseline",
},
"attack": {
"type": args.attack,
"magnitude": magnitude,
"duration_min": args.duration_min,
"tooling": tooling,
},
"blast_radius": {
"scope": args.blast_radius or "<must define before experiment>",
"rollback_immediately_if": args.abort_if or "<must define abort criteria>",
},
"abort_criteria": _parse_abort_criteria(args.abort_if),
"rollback_procedure": args.rollback or "Disable fault injection; verify steady state recovers within 2 minutes.",
"monitoring_dashboard": args.dashboard or "<paste dashboard URL>",
"owner": args.owner or "<assign owner>",
"on_call_acknowledged": False,
"learning_question": args.learning or "What did we learn that we did not know before?",
}
return plan
def _parse_abort_criteria(raw):
if not raw:
return []
parts = [p.strip() for p in raw.split(" OR ")]
return [{"signal": p, "action": "abort"} for p in parts if p]
def render_markdown(plan):
lines = []
lines.append(f"# Chaos Experiment: {plan['experiment_id']}")
lines.append("")
lines.append(f"- **Target:** `{plan['target']}`")
lines.append(f"- **Created:** {plan['created']}")
lines.append(f"- **Owner:** {plan['owner']}")
lines.append("")
lines.append("## Hypothesis")
lines.append(f"> {plan['hypothesis']}")
lines.append("")
lines.append("## Steady-state metric")
lines.append(f"- **Metric:** {plan['steady_state']['metric']}")
lines.append(f"- **Baseline window:** {plan['steady_state']['baseline_window']}")
lines.append(f"- **Tolerance:** {plan['steady_state']['tolerance']}")
lines.append("")
lines.append("## Attack")
a = plan["attack"]
lines.append(f"- **Type:** {a['type']}")
lines.append(f"- **Magnitude:** {a['magnitude']}")
lines.append(f"- **Duration:** {a['duration_min']} minutes")
lines.append(f"- **Tooling:** {a['tooling']}")
lines.append("")
lines.append("## Blast radius")
lines.append(f"- **Scope:** {plan['blast_radius']['scope']}")
lines.append("")
lines.append("## Abort criteria")
if plan["abort_criteria"]:
for c in plan["abort_criteria"]:
lines.append(f"- {c['signal']}")
else:
lines.append("- **WARNING: no abort criteria defined — DO NOT RUN**")
lines.append("")
lines.append("## Rollback procedure")
lines.append(plan["rollback_procedure"])
lines.append("")
lines.append("## Monitoring")
lines.append(f"- Dashboard: {plan['monitoring_dashboard']}")
lines.append("")
lines.append("## Learning question")
lines.append(f"> {plan['learning_question']}")
return "\n".join(lines)
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--target", required=True, help="Target system or service")
ap.add_argument("--hypothesis", required=True, help='Hypothesis: "When X, metric Y stays Z"')
ap.add_argument("--attack", required=True, choices=list(ATTACK_DEFAULTS.keys()))
ap.add_argument("--magnitude", help="Attack magnitude (default: per-attack hint)")
ap.add_argument("--duration-min", type=int, default=15)
ap.add_argument("--steady-metric", help="Steady-state metric name (e.g., 'p99 latency')")
ap.add_argument("--tolerance", help="Tolerance vs baseline (e.g., 'within ±5%%')")
ap.add_argument("--blast-radius", help="Blast radius (e.g., '5%% of US traffic')")
ap.add_argument("--abort-if", dest="abort_if", help='Abort criteria, OR-separated (e.g., "p99 > 1000ms OR error_rate > +1pp")')
ap.add_argument("--rollback", help="Rollback procedure")
ap.add_argument("--tooling", help="Chaos tool to use (default: per-attack hint)")
ap.add_argument("--dashboard", help="Monitoring dashboard URL")
ap.add_argument("--owner", help="Experiment owner")
ap.add_argument("--learning", help="Learning question")
ap.add_argument("--format", choices=["markdown", "json"], default="markdown")
args = ap.parse_args()
plan = build_plan(args)
if args.format == "json":
print(json.dumps(plan, indent=2))
else:
print(render_markdown(plan))
return 0 if plan["abort_criteria"] else 1
if __name__ == "__main__":
sys.exit(main())
#!/usr/bin/env python3
"""Generate a structured chaos experiment postmortem.
Takes an experiment plan (JSON from experiment_designer.py) plus a results
file (free-form text or structured key=value lines), and produces a markdown
postmortem with hypothesis verdict, learning, surprises, and follow-up actions.
Catches common postmortem failure modes: no learning, no follow-up, blame-laden
language.
"""
import argparse
import json
import os
import re
import sys
from datetime import datetime, timezone
BLAME_PHRASES = [
"fault of",
"should have known",
"stupid",
"incompetent",
"obvious",
"lazy",
"didn't bother",
]
REQUIRED_RESULT_FIELDS = {
"outcome": "Did the hypothesis hold? (held|refuted|inconclusive)",
"duration_actual_min": "Actual experiment duration in minutes",
"aborted": "Was the experiment aborted? (true|false)",
}
def _parse_results(path):
"""Parse a results file. Lines like 'key=value' OR free text. Returns dict."""
if not os.path.isfile(path):
return {"_raw_text": ""}
with open(path, "r", encoding="utf-8", errors="replace") as f:
text = f.read()
parsed = {}
for line in text.splitlines():
m = re.match(r"^\s*([\w_.\-]+)\s*=\s*(.+?)\s*$", line)
if m:
parsed[m.group(1)] = m.group(2)
parsed["_raw_text"] = text
return parsed
def _check_blame(text):
found = []
low = text.lower()
for phrase in BLAME_PHRASES:
if phrase in low:
found.append(phrase)
return found
def build_postmortem(plan, results, follow_ups):
raw_text = results.get("_raw_text", "")
blame = _check_blame(raw_text)
pm = {
"experiment_id": plan.get("experiment_id", "?"),
"target": plan.get("target", "?"),
"created": datetime.now(timezone.utc).isoformat(),
"hypothesis": plan.get("hypothesis", "?"),
"outcome": results.get("outcome", "<UNRECORDED — must record>"),
"aborted": results.get("aborted", "<unrecorded>"),
"duration_actual_min": results.get("duration_actual_min", "<unrecorded>"),
"duration_planned_min": plan.get("attack", {}).get("duration_min", "?"),
"what_we_learned": results.get("learned", "<UNRECORDED — must record at least one learning>"),
"what_surprised_us": results.get("surprised", "<unrecorded>"),
"what_failed": results.get("failed", "<none recorded>"),
"what_held": results.get("held", "<none recorded>"),
"follow_ups": follow_ups,
"blame_warnings": blame,
"raw_results_excerpt": raw_text[:500],
}
return pm
def render_markdown(pm):
lines = []
lines.append(f"# Postmortem: {pm['experiment_id']}")
lines.append("")
lines.append(f"- **Target:** `{pm['target']}`")
lines.append(f"- **Postmortem date:** {pm['created']}")
lines.append(f"- **Outcome:** {pm['outcome']}")
lines.append(f"- **Aborted:** {pm['aborted']}")
lines.append(f"- **Duration:** planned={pm['duration_planned_min']}min, actual={pm['duration_actual_min']}min")
lines.append("")
lines.append("## Hypothesis")
lines.append(f"> {pm['hypothesis']}")
lines.append("")
lines.append("## What we learned")
lines.append(pm["what_we_learned"])
lines.append("")
lines.append("## What surprised us")
lines.append(pm["what_surprised_us"])
lines.append("")
lines.append("## What failed")
lines.append(pm["what_failed"])
lines.append("")
lines.append("## What held")
lines.append(pm["what_held"])
lines.append("")
lines.append("## Follow-up actions")
if pm["follow_ups"]:
for f in pm["follow_ups"]:
lines.append(f"- [ ] {f}")
else:
lines.append("- _none recorded — every experiment should produce ≥1 follow-up_")
if pm["blame_warnings"]:
lines.append("")
lines.append("## ⚠️ Blame warning")
lines.append("Blame-laden language detected — postmortems should be blameless.")
for b in pm["blame_warnings"]:
lines.append(f"- '{b}'")
return "\n".join(lines)
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--plan", required=True, help="Path to experiment plan JSON (from experiment_designer.py --format json)")
ap.add_argument("--result-log", required=True, help="Path to result log (free-form text OR key=value lines)")
ap.add_argument("--follow-up", action="append", default=[], help="A follow-up action; repeat for multiple")
ap.add_argument("--format", choices=["markdown", "json"], default="markdown")
args = ap.parse_args()
if not os.path.isfile(args.plan):
print(f"ERROR: plan not found: {args.plan}", file=sys.stderr)
return 2
with open(args.plan, "r", encoding="utf-8") as f:
plan = json.load(f)
results = _parse_results(args.result_log)
pm = build_postmortem(plan, results, args.follow_up)
if args.format == "json":
print(json.dumps(pm, indent=2))
else:
print(render_markdown(pm))
return 0
if __name__ == "__main__":
sys.exit(main())
Related skills
How it compares
Pick chaos-engineering when you need hypothesis-driven fault injection with blast-radius math; use load-testing skills when the goal is capacity, not failure-mode discovery.
FAQ
What Python tools does chaos-engineering include?
chaos-engineering bundles three stdlib-only Python scripts: experiment_designer.py for structured plans, blast_radius_calculator.py for GREEN/YELLOW/RED risk scoring, and experiment_postmortem.py for markdown learning reports from experiment.json and result logs.
Which chaos tools does chaos-engineering support?
chaos-engineering maps seven attack types to Chaos Toolkit, Chaos Mesh, Litmus, Gremlin, and AWS FIS, plus tc, Toxiproxy, stress-ng, and service-mesh fault injection patterns for latency, partition, and infrastructure-kill scenarios.
When should developers skip chaos-engineering?
chaos-engineering targets proactive resilience experiments, not incident response, threat hunting, or capacity load testing. Skip it when debugging an active outage or measuring throughput instead of failure-mode behavior.