Chaos Testing
Netflix chaos engineering patterns for testing application resilience. Inject random errors and latency to verify that circuit breakers, fallback chains, and error boundaries work under stress.
Safety: All chaos functions are disabled by default (enabled: false). You must explicitly enable them. Never enable chaos in production.
Install from registry
npx shadcn@latest add https://registry.mukoko.com/api/v1/ui/chaosLive demonstration
Chaos injection combined with circuit breaker. Adjust the error rate and latency, then send requests to see how the system responds under stress.
Adjust chaos parameters and send requests to see resilience in action. The circuit breaker will open after 3 chaos-injected failures.
Chaos injection + circuit breaker working together. Crank up the error rate to see the circuit open, then watch it recover through HALF_OPEN after 5 seconds.
withChaos
Wrap any async operation with configurable error and latency injection. Passes through unchanged when disabled.
import { withChaos, ChaosError } from "@/lib/chaos"
// Only in development/testing
const data = await withChaos(
() => fetch("/api/weather").then(r => r.json()),
{
enabled: process.env.NODE_ENV === "development",
errorRate: 0.3, // 30% chance of failure
latencyMs: [100, 500], // 100-500ms random delay
}
)
// Distinguish chaos errors from real errors
try {
await withChaos(() => fetchData(), { enabled: true, errorRate: 0.5 })
} catch (error) {
if (error instanceof ChaosError) {
console.log("Chaos injected:", error.chaosType) // "error" | "latency"
console.log(error.injected) // true
}
}chaosMiddleware
Create a reusable chaos wrapper with fixed configuration.
import { chaosMiddleware } from "@/lib/chaos"
const devChaos = chaosMiddleware({
enabled: process.env.CHAOS_TESTING === "true",
errorRate: 0.2,
latencyMs: [50, 200],
})
// Wrap any operation
const weather = await devChaos(() => fetchWeather(slug))
const user = await devChaos(() => getUser(id))
const report = await devChaos(() => submitReport(data))Combined with circuit breaker
The real power is testing your resilience stack end-to-end: chaos injects failures, the circuit breaker opens, the fallback chain kicks in.
import { withChaos } from "@/lib/chaos"
import { CircuitBreaker, PROVIDER_CONFIGS } from "@/lib/circuit-breaker"
import { withFallback } from "@/lib/fallback-chain"
import { withRetry } from "@/lib/retry"
const breaker = new CircuitBreaker({
name: "weather-api",
...PROVIDER_CONFIGS["tomorrow-io"],
})
// Chaos wraps the innermost call
const weather = await withFallback([
{
name: "primary",
execute: () => withRetry(
() => breaker.execute(() =>
withChaos(() => fetchPrimary(), {
enabled: true,
errorRate: 0.5, // 50% failure rate
})
),
{ maxAttempts: 2 }
),
timeoutMs: 10000,
},
{
name: "cache",
execute: () => getFromCache(),
},
])
// Verify:
// 1. Chaos injects failures in primary
// 2. Retry attempts 2x before giving up
// 3. Circuit breaker opens after 3 total failures
// 4. Fallback chain falls through to cacheTesting patterns
- Start with low error rates (10-20%) and increase gradually
- Test each layer independently first, then the full stack
- Verify circuit breaker opens at the expected failure threshold
- Confirm fallback chain reaches the last stage under heavy chaos
- Check that error boundaries show appropriate fallback UI
- Monitor memory with useMemoryPressure during chaos testing
- Never enable chaos in production — use environment variables