> Chaos engineering, fault injection, and resilience validation. Systematically introduces failures to verify that systems degrade gracefully, recover automatically, and maintain availability under real-world fault conditions.
Installs into .claude/skills of the current project.
Are you the author of Harness Chaos?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/intense-visions-harness-chaos)
# Harness Chaos
> Chaos engineering, fault injection, and resilience validation. Systematically introduces failures to verify that systems degrade gracefully, recover automatically, and maintain availability under real-world fault conditions.
## When to Use
- Validating resilience of a service before a production launch or major milestone
- Testing circuit breakers, retries, timeouts, and fallback mechanisms under failure
- Preparing for a game day exercise with defined failure scenarios
- NOT when the service has no resilience mechanisms implemented (implement them first with harness-resilience)
- NOT when testing application logic correctness (use harness-tdd or harness-integration-test instead)
- NOT when performing load or performance testing (use harness-load-testing instead)
## Process
### Phase 1: PLAN -- Define Failure Modes and Steady-State Hypotheses
1. **Map the system architecture.** Identify:
- Services and their dependencies (databases, caches, message queues, third-party APIs)
- Network boundaries (service-to-service calls, DNS resolution, load balancers)
- Infrastructure components (Kubernetes pods, Docker containers, cloud services)
- Data stores and their replication topology
2. **Define steady-state behavior.** Establish measurable indicators of normal operation:
- Response time P50 and P99 (e.g., P99 < 500ms)
- Error rate (e.g., < 0.1% 5xx responses)
- Throughput (e.g., > 100 requests/second)
- Business metrics (e.g., orders processed per minute, messages delivered)
3. **Enumerate failure modes.** For each dependency, define what can go wrong:
- **Network failures:** latency injection, packet loss, DNS resolution failure, connection reset
- **Service failures:** dependency returns 500, dependency is unreachable, dependency responds slowly
- **Infrastructure failures:** pod crash, node failure, disk full, memory exhaustion
- **Data failures:** database connection pool exhaustion, replication lag, cache eviction storm
4. **Scope the blast radius.** For each experiment, define:
- **Target:** which specific service or instance is affected
- **Duration:** how long the fault persists (start small: 30 seconds to 2 minutes)
- **Magnitude:** what percentage of traffic or instances are affected
- **Abort conditions:** when to immediately stop the experiment (e.g., customer-visible error rate > 5%)
5. **Prioritize experiments by risk and value.** Start with:
- High likelihood, high impact failures (database connection loss, primary cache failure)
- Failures with existing but untested resilience mechanisms (circuit breakers, retries)
- Failures that have caused production incidents historically
6. **Document the experiment plan.** For each experiment, write:
- Hypothesis: "When [fault] is injected into [target], the system [expected behavior]"
- Steady-state metric: what to monitor
- Abort criteria: when to terminate the experiment
- Rollback procedure: how to immediately restore normal operation
### Phase 2: INJECT -- Apply Fault Injection
1. **Select the chaos tooling.** Based on the infrastructure:
- **Chaos Toolkit:** framework-agnostic, declarative experiment definitions (JSON/YAML)
- **Gremlin:** SaaS platform for enterprise chaos engineering
- **Litmus:** Kubernetes-native chaos engineering
- **Toxiproxy:** network-level fault injection proxy
- **tc (traffic control):** Linux kernel network delay/loss injection
- **Custom middleware:** application-level fault injection via feature flags or interceptors
2. **Configure the experiment.** Write the experiment definition:
- Steady-state probe: how to verify normal operation before and after
- Fault action: what fault to inject (latency, error, crash, resource exhaustion)
- Rollback action: how to undo the fault if the experiment must abort
- Duration and magnitude parameters
3. **Verify the pre-experiment steady state.** Before injecting any fault:
- Run the steady-state probe and confirm it passes
- Record baseline metrics (latency, error rate, throughput)
- Confirm monitoring and alerting are active and visible to the experiment operator
4. **Inject the fault.** Execute the experiment:
- Start with the smallest blast radius (single instance, short duration)
- Monitor real-time metrics during injection
- Be prepared to abort immediately if abort criteria are met
5. **Verify the abort mechanism works.** Before running experiments with larger blast radius:
- Test that the rollback action successfully removes the injected fault
- Confirm the system returns to steady state after rollback
- Measure recovery time
### Phase 3: OBSERVE -- Monitor System Behavior Under Fault
1. **Collect metrics during the experiment.** Capture:
- Response latency distribution (P50, P95, P99) for the affected service and its consumers
- Error rates (HTTP status codes, exception counts, queue dead letters)
- Circuit breaker state transitions (closed, open, half-open)
- Retry counts and backoff behavior
- Resource utilization (CPU, memory, connections, threads)
2. **Verify the steady-state hypothesis.** Compare observed metrics against the hypothesis:
- **Hypothesis holds:** the system degraded gracefully as expected. Record as a successful experiment.
- **Hypothesis violated:** the system behaved worse than expected. This is a finding. Record the specific deviation.
3. **Check for cascading failures.** Monitor downstream services:
- Did the fault in service A cause service B to fail?
- Did retry storms amplify the failure?
- Did the load balancer route traffic away from the faulty instance?
- Did the circuit breaker open before the caller's timeout?
4. **Record the timeline.** Document:
- T+0: fault injected
- T+N: first detection by monitoring/alerting
- T+N: circuit breaker opens (if applicable)
- T+N: system reaches degraded but stable state
- T+N: fault removed
- T+N: system returns to full steady state (recovery time)
5. **Terminate the experiment.** Remove the injected fault and verify:
- The system returns to steady state within the expected recovery time
- No data loss or corruption occurred during the experiment
- All queued/retried operations complete successfully
### Phase 4: IMPROVE -- Analyze Findings and Strengthen Resilience
1. **Classify findings.** For each experiment:
- **Passed:** system behavior matched the hypothesis. No action needed.
- **Finding -- minor:** system recovered but slower than expected. Add to backlog.
- **Finding -- major:** system experienced partial outage or data issue. Immediate action required.
- **Finding -- critical:** cascading failure or data loss. Stop experiments and prioritize fix.
2. **Recommend resilience improvements.** For each finding:
- Missing circuit breaker: implement circuit breaker with appropriate thresholds
- Insufficient timeout: reduce timeout to prevent thread pool exhaustion
- No fallback: implement graceful degradation (cached response, default value, feature flag)
- Retry storm: implement exponential backoff with jitter and retry budget
- No health check: add readiness/liveness probes that detect the specific failure mode
3. **Update runbooks and incident response documentation.** For each experiment:
- Add the failure mode to the incident response playbook
- Document the detection time, recovery procedure, and expected recovery time
- Update alerting thresholds if monitoring did not detect the failure promptly
4. **Plan follow-up experiments.** Based on findings:
- Re-run failed experiments after resilience improvements are implemented
- Increase blast radius for successful experiments (from single instance to multiple instances)
- Combine faults (e.g., database slowdown plus cache failure simultaneously)
5. **Run `harness validate`.** Confirm the project passes all harness checks after any code changes made for resilience improvements.
6. **Generate an experiment report.** Summarize:
- Experiments run, hypotheses tested, and pass/fail outcomes
- Findings with severity classification
- Resilience improvements recommended or implemented
- Recovery time measurements
- Recommended next experiments
### Graph Refresh
If a knowledge graph exists at `.harness/graph/`, refresh it after code changes to keep graph queries accurate:
```
harness scan [path]
```
## Harness Integration
- **`harness validate`** -- Run in IMPROVE phase after resilience changes are implemented. Confirms project health.
- **`harness check-deps`** -- Run after INJECT phase setup to verify chaos tooling dependencies do not leak into production bundles.
- **`emit_interaction`** -- Used at checkpoints to present experiment plans for human approval before fault injection, and to present findings for prioritization.
- **Grep** -- Used in PLAN phase to find circuit breaker configurations, retry policies, timeout settings, and fallback implementations.
- **Glob** -- Used to locate infrastructure configuration files, Kubernetes manifests, and Docker Compose definitions.
## Success Criteria
- Every critical dependency has at least one chaos experiment testing its failure mode
- Steady-state hypotheses are defined with measurable metrics before experiments run
- The system degrades gracefully under every tested fault (no cascading failures, no data loss)
- Recovery time after fault removal is measured and meets the defined SLA
- Findings are documented with severity, root cause, and recommended fix
- Abort mechanisms are tested and confirmed functional before expanding blast radius
- `harness validate` passes after resilience improvements
## Examples
### Example: Chaos Toolkit Experiment for Database Latency
**PLAN -- Experiment definition:**
```json
{
"title": "Database latency does not cause cascading timeout failures",
"description": "Inject 2-second latency on PostgreSQL connections and verify the order service responds within 5 seconds using cached data",
"steady-state-hypothesis": {
"title": "Order service responds within SLA",
"probes": [
{
"type": "probe",
"name": "order-api-responds",
"tolerance": true,
"provider": {
"type": "http",
"url": "http://localhost:3000/api/orders/health",
"timeout": 5
}
}
]
},
"method": [
{
"type": "action",
"name": "inject-db-latency",
"provider": {
"type": "process",
"path": "toxiproxy-cli",
"arguments": "toxic add -t latency -a latency=2000 postgresql"
}
},
{
"type": "probe",
"name": "check-order-response-time",
"provider": {
"type": "http",
"url": "http://localhost:3000/api/orders?limit=10",
"timeout": 5
}
}
],
"rollbacks": [
{
"type": "action",
"name": "remove-db-latency",
"provider": {
"type": "process",
"path": "toxiproxy-cli",
"arguments": "toxic remove -n inject-db-latency_latency_downstream postgresql"
}
}
]
}
```
### Example: Litmus Chaos for Kubernetes Pod Failure
**INJECT -- Pod kill experiment:**
```yaml
# litmus/pod-kill-experiment.yaml
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
name: order-service-pod-kill
namespace: staging
spec:
appinfo:
appns: staging
applabel: app=order-service
appkind: deployment
chaosServiceAccount: litmus-admin
experiments:
- name: pod-delete
spec:
components:
env:
- name: TOTAL_CHAOS_DURATION
value: '60'
- name: CHAOS_INTERVAL
value: '10'
- name: FORCE
value: 'false'
- name: PODS_AFFECTED_PERC
value: '50'
```
**OBSERVE -- Expected behavior timeline:**
```
T+0s: Fault injected - 50% of order-service pods killed
T+3s: Kubernetes detects pod failure, starts replacement pods
T+5s: Load balancer routes traffic to surviving pods
T+8s: Response latency increases from 50ms to 200ms (surviving pods absorb load)
T+15s: Replacement pods pass readiness probe, rejoin the pool
T+20s: Latency returns to baseline (50ms)
T+60s: Experiment ends
Result: PASSED - System maintained availability throughout.
P99 latency spiked to 450ms (within 500ms SLA).
Zero 5xx errors observed. No data loss.
```
## Rationalizations to Reject
| Rationalization | Reality |
| ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| "Our circuit breakers are already tested in unit tests — we don't need chaos experiments" | Unit tests verify that circuit breaker code executes. Chaos experiments verify that the circuit breaker actually opens under real load, that the fallback produces an acceptable user-facing response, and that monitoring detects the transition. These are different things. |
| "We can't run chaos experiments in production — it's too risky" | Avoiding chaos experiments does not reduce risk — it defers the discovery of failure modes to real incidents. Chaos experiments in staging with defined abort criteria and short durations are lower risk than discovering failure modes at 2am during a real outage. |
| "The experiment passed in staging so we know it'll work in production" | Staging differences in traffic volume, data distribution, and infrastructure scale can mask failure modes. Staging experiments validate the mechanism; production experiments (with tightly scoped blast radius) validate the system under real conditions. |
| "We injected the fault and the system recovered — the experiment is done" | Recovery alone does not validate resilience. The experiment must also confirm: detection time (did monitoring catch it?), recovery time (did it meet the SLA?), and no data loss or corruption. A system that recovers after 10 minutes of data inconsistency has not passed. |
| "We have runbooks for these failure modes, so game days aren't necessary" | A runbook that has never been executed under pressure is a hypothesis, not a procedure. Game days reveal whether runbooks are complete, whether on-call engineers can execute them accurately under stress, and whether the estimated recovery times are realistic. |
## Gates
- **No chaos experiments without abort criteria.** Every experiment must define conditions under which it is immediately terminated. Running an experiment that you cannot stop is reckless, not engineering.
- **No production experiments without staging validation.** Run every experiment in staging first. Only after the experiment passes in staging and the team is confident in the abort mechanism should it be considered for production.
- **No expanding blast radius without successful small-scope runs.** Start with a single instance or a small percentage of traffic. Only increase scope after the smaller experiment passes and recovery is confirmed.
- **No experiments during incidents or peak traffic.** Chaos experiments must not be run when the system is already under stress or during known high-traffic periods. Schedule experiments during low-traffic windows.
## Escalation
- **When an experiment causes unexpected data loss or corruption:** Immediately abort, restore from backup, and halt all chaos experiments. Conduct a post-mortem to understand why the data protection mechanisms failed. Do not resume experiments until the data safety gap is addressed.
- **When monitoring does not detect the injected fault:** This is a finding -- the monitoring is inadequate. Do not interpret "no alerts" as "the system handled it well." Escalate to the observability team to add detection for the specific failure mode before re-running the experiment.
- **When the team is reluctant to run chaos experiments:** Start with the least risky experiment in a non-production environment. Use the results to demonstrate value. Chaos engineering requires organizational buy-in; do not force it.
- **When cascading failures are discovered:** This is a critical finding. The service mesh or dependency chain lacks isolation. Escalate immediately with a recommendation for circuit breakers, bulkheads, or service mesh fault injection policies.