matthiasnoback/random-disaster-bundle
A Symfony bundle that randomly injects exceptions into your app so you can test how it behaves when things go wrong. Set a failure rate and RandomDisasterBundle throws unexpected errors at random points during requests—ideal for verifying error pages, logging, and monitoring before real incidents.
The first thing to internalise: this is a Symfony bundle, not a Laravel package. There is no service provider, no facade, no config/chaos.php shim. A Laravel developer "getting started" here is really getting started on the pattern, not the artifact. Two legitimate entry points:
Path A — study the source as a reference design. Clone the repo and read src/ end to end; it is small. Specifically look at:
Disaster interface (the extension point — one method, "do something bad").kernel.request (and possibly kernel.response) — this is the entire bundle in a nutshell.DependencyInjection/Configuration.php for the exact config tree (probability key, options) — this varies by version, so read it rather than trusting blog posts.Path B — if you do maintain a Symfony app, install it properly:
composer require --dev matthiasnoback/random-disaster-bundle
Register it only for the environments where you actually want chaos — typically dev, test, and a dedicated staging:
// config/bundles.php
return [
// ...
MatthiasNoback\RandomDisasterBundle\RandomDisasterBundle::class => ['dev' => true, 'test' => true],
];
Then set a low probability in the environment config:
# config/packages/dev/random_disaster.yaml
random_disaster:
probability: 0.05
First use case: enable at ~1–5% in staging, hit a normally healthy route repeatedly, and check three things — is your custom 500 page rendered, does the exception land in Sentry/Rollbar with a usable stack trace, and does your alert fire? If any of those three is a shrug, you have just found real work.
Path C — the one most Laravel teams should take: don't install anything. Spend half a day reading the source, then write ~80 lines of Laravel-native middleware (see next section). You get the same capability with zero Symfony dependency footprint.
Symfony: custom disasters. Implement the Disaster interface in a service, tag it (autoconfigured), and let the bundle's random trigger decide whether to invoke one per request. Keep each disaster single-purpose — one throws, one returns an error response, one might log a warning — so the probability math stays legible.
Staging soak (highest-value pattern). Set probability to 0.01–0.05 in staging and leave it on permanently. Every few dozen requests you get a free rehearsal of your error path. This is the pattern the bundle is genuinely good at, and it maps 1:1 onto a Laravel reimplementation.
Laravel reimplementation shape. The idiomatic port, which is what most Laravel developers will actually build:
// app/Http/Middleware/ChaosMiddleware.php
public function handle($request, Closure $next)
{
if (! config('chaos.enabled') || app()->environment('production')) {
return $next($request);
}
if (! in_array($request->path(), config('chaos.allowlist'), true)) {
return $next($request);
}
if ($this->seededRandom() > config('chaos.probability')) {
return $next($request);
}
return match (config('chaos.mode')) {
'exception' => throw new \RuntimeException('Chaos: injected failure'),
'latency' => tap($next($request), fn () => usleep(config('chaos.latency_us'))),
'http_error' => abort(random_int(400, 599)),
};
}
Backed by config/chaos.php with enabled, probability, allowlist, mode, and a seed for determinism. Register the middleware globally in bootstrap/app.php (Laravel 11 style) or Kernel.php, and gate registration on the env flag so it is genuinely absent in production rather than merely inert.
Integration tips.
config('chaos.enabled')) so the kill switch is a single config/env flip, not a code deploy./api/checkout is a test; injection on /oauth/callback is an incident.Log::info. The bundle's whole premise is "does the alert fire" — a chaos event that only writes to laravel.log proves nothing.http_error mode plus a job dispatch is a useful combo.It will not install in Laravel. Even --dev is a mistake — the Symfony bundle pulls in symfony/http-kernel, symfony/dependency-injection, and friends, which will resolve-conflict against a modern Laravel app's PHP/Symfony constraints and send a misleading supply-chain signal. Treat the repo as read-only reference material. The 0-dependents / 114-stars / single-maintainer profile confirms this was never intended as infrastructure; expect PHP 7.x-era constraints and Symfony 2.x/3.x assumptions.
The fault model is thin. It throws random exceptions and returns error responses. It does not simulate latency, upstream timeouts, partial dependency outages, DB failover, or queue backlog. If your resilience question is "what happens when the payment gateway is slow," this bundle — and the naive port of it — cannot answer it. Reach for Toxiproxy, Gremlin, LitmusChaos, or AWS FIS at that point.
Probability is per-request, not per-session. At 0.05, a high-traffic route fails 1-in-20 requests — that is a lot of red on a dashboard, and it will page someone. Scope by route allowlist and consider a per-session or per-IP gate if you want a single user to see a whole failed journey.
The kill switch must be an env var, not just YAML. The classic failure mode is "chaos flag stayed on after the drill." Prefer CHAOS_ENABLED=false set in the production environment file, plus a hard app()->environment('production') guard in code, plus a CI check that greps for chaos.enabled: true in production config. Belt, braces, and a tripwire.
Non-determinism will bite your tests. Random faults produce flaky tests. If you port this, thread a seed through (config('chaos.seed')) and use mt_srand()/a seeded generator so ChaosMiddlewareTest asserts exact behaviour. Default enabled to false in phpunit.xml.
Documentation is the real deliverable. The code is trivial; the organisational ramp-up is not. Teams need to know what "chaos enabled in staging" means for their QA run, who is allowed to flip it, and how to interpret the resulting alerts as signal rather than noise. Write the runbook before you write the middleware.
Configuration tree is version-sensitive. The bundle's config keys (probability, and whatever else exists) are not stable across versions and are not documented on the README. Always read DependencyInjection/Configuration.php from the exact tagged version you are looking at — do not copy config snippets from blog posts.
Extension point worth stealing. The Disaster interface + DI tag is a genuinely clean design: a set of small, independently-tagged failure injectors rather than one god-mode middleware. If your internal implementation grows beyond three modes, mirror that — a ChaosAction contract with autoconfigured implementations maps neatly onto Laravel's container tags.
How can I help you explore Laravel packages today?