The question this answers
If this component disappears right now, what exactly stops working — and what have I forgotten?
A blast-radius analysis guarantees only a lower bound: the set of things you can show will fail. It cannot guarantee an upper bound, because the couplings you did not draw are precisely the ones you do not know about. Treat the result as "at least this much", never as "only this much".
Everything below is bought to hold this sentence. "Strongly consistent" with no scope attached is a slogan, not a guarantee — read what it actually covers, and what it explicitly does not.
A node knows which endpoints it calls. It does not know what it shares — that its connection pool, DNS resolver, sidecar, node-local disk, IAM role, config service or TLS certificate authority is also on the path of six other services. Shared-fate coupling is invisible from inside any single node, which is why blast radius cannot be computed from any one service’s view and has to be assembled deliberately from the outside.
A node knows its own state and the messages that arrived. Everything else is inference from evidence that was already stale. "B has not replied in five seconds" is knowledge; "B is down" is a decision — and usually the bug.
The procedure is boring and that is the point
Take the dependency graph. For each node, delete it, and walk the graph asking what still works. Write the answer down as a set of *user-visible features*, not as a set of services — "checkout fails" is a result, "order-service is unavailable" is a restatement of the premise.
Do it for every node, not just the ones that feel important. The interesting results are never at the nodes labelled critical, which everybody already knows about; they are at the small shared ones. The config service that every pod reads at startup. The internal certificate authority. The single DNS zone. The feature-flag service that fails closed. The auth service that every request touches and that nobody lists as a dependency because "it is just auth".
Then rank by product impact and by *ease of failure*, not just by importance. A component that is critical but has three replicas across zones and a tested failover is a smaller risk than an unimportant-sounding component that is a single instance somebody set up in 2021.
The answer is worse than expected, and here is why
Five reasons the real blast radius exceeds the drawn one, in rough order of how often they bite.
Shared infrastructure. Two services that never call each other still share a database host, a connection pool, a Kubernetes node, a NAT gateway, a load balancer, or a log-shipping sidecar that blocks writes when the collector is down. None of that is on the service diagram, because the diagram describes calls, not fate.
Startup dependencies differ from steady-state ones. A service that reads config at boot has no runtime dependency on the config service — until every pod restarts at once during an unrelated deploy, and then the config service is on the critical path for the entire fleet simultaneously. This is the single most under-modelled coupling in modern systems, because it is invisible in normal operation and total during recovery.
Transitive depth. Teams reason one hop out. Checkout depends on pricing; pricing depends on a currency service; the currency service depends on a cache that depends on a cluster that shares a control plane with something else. Three hops is where the surprises live.
Failure of the control plane, not the data plane. Services keep serving when the control plane dies — until something needs to change. Then no deploy, no scale-up, no failover, no cert rotation. The blast radius of a control plane is "everything, but only when you need it", which is exactly during an incident.
The observability path. If your metrics, logs and alerting traverse the failing component, its blast radius includes your ability to see the failure. A dependency graph that does not include the monitoring path is describing a system you will be debugging blind.
| What it looks like | When it bites | What the operator sees | |
|---|---|---|---|
| Shared connection pooltypical | Two dependencies, one pool | One dependency slows | Calls to a healthy dependency time out |
| Startup-only dependencytypical | Config or secrets read at boot | A fleet-wide restart | Nothing starts; the component was "not a runtime dependency" |
| Control planetypical | Orchestrator, deploy pipeline, IAM | Only when something must change | The system serves fine and cannot be fixed |
| Observability pathtypical | Metrics or logs through the failing component | Immediately | Dashboards go blank — and blank looks like healthy |
| Certificate authoritytypical | One internal CA for mTLS | Expiry, or CA unavailability at renewal | Every service fails to talk to every other service at once |
| Single DNS zonetypical | All internal resolution | Resolver or zone failure | Every call fails with a name error and nothing looks unhealthy |
From radius to action
An analysis that produces a diagram and no decisions was a drawing exercise. Each oversized radius has exactly four possible responses, and naming which one you chose is the deliverable.
Shrink it — split the shared thing so its failure covers less: separate pools per dependency, per-tenant shards, one cell per region. Contain it — accept the failure but bound what it takes with it, via [[resource-isolation]] and [[failure-containment]]. Degrade around it — fill in the row of the degradation matrix so its failure removes a feature rather than the product. Accept it — write down that this component takes everything with it, and that this is a known, priced decision rather than an oversight.
Accepting is a legitimate answer and should be used more often than it is. A single internal CA taking the whole system down may be entirely acceptable if it has never failed, has a tested rotation, and splitting it would cost a quarter of engineering time. What is not acceptable is not knowing.
- Shrink: split the shared resource so one failure covers fewer consumers.
- Contain: bulkhead so the failure cannot consume resources belonging to others.
- Degrade: define what the product does without it, in advance.
- Accept: record the decision, its reasoning and its owner, so it is a choice rather than a surprise.
Verifying the answer instead of asserting it
A blast-radius analysis is a set of hypotheses about what fails together, and hypotheses about a live system are exactly what [[steady-state-hypothesis]] exists to test. "We believe removing the recommendation service affects only the carousel" is a testable claim with a small blast radius and a clean abort condition — an ideal first experiment.
The claims worth testing first are the ones where the analysis says "nothing else is affected", because that is the claim most likely to be wrong and least likely to be discovered by reading. The ones that say "everything dies" rarely need testing; they need a decision.
A useful cadence: re-derive the graph when the architecture changes, and test one previously untested "nothing else is affected" claim per cycle. The graph decays — every new shared cache, sidecar or platform component adds edges nobody drew.
Key points
- For each node in the dependency graph, ask what user-visible features stop working if it disappears.
- The real radius is larger than the drawn one, because the drawn graph shows calls and failures propagate through shared fate.
- Startup-only dependencies are invisible in normal operation and total during a fleet restart.
- A control plane’s blast radius is "everything, but only when you need to change something" — which is during an incident.
- If observability traverses the failing component, the radius includes your ability to see the failure.
- Every oversized radius gets one of four answers: shrink, contain, degrade, or accept explicitly.
The chain, answered
Every field here is required, which is why no lesson in this domain can recommend a design without naming what an operator sees when it fails, what survives the partition, what repairs it afterwards, and the simpler thing to consider first.
- • Enumerate components, including infrastructure ones that appear on no service diagram.
- • For each, delete it from the graph and walk outward, recording which user-visible features stop.
- • Add the invisible edges deliberately: shared pools, shared hosts, startup reads, DNS, certificates, IAM, observability paths.
- • Rank by product impact multiplied by likelihood of failing, not by perceived importance.
- • Choose shrink, contain, degrade or accept for each oversized radius, and record the owner.
- • Test the "nothing else is affected" claims with bounded experiments.
- • The graph is derived from documentation rather than from traffic, and is therefore a description of the system as designed rather than as running.
- • Infrastructure dependencies are omitted because they belong to a different team’s diagram.
- • The analysis is done once and never refreshed, so it describes an architecture two years old.
- • Ranking uses perceived importance, so the single-instance utility service nobody thinks about is never examined.
- • The conclusion is a diagram with no decision attached, so nothing changes.
- • Surprise co-failure: the operator sees two services fail together that have no call path between them, and spends the first twenty minutes looking for a link that does not exist in the code — the link is a shared node pool.
- • Fleet-restart deadlock: the operator sees nothing come back after a rolling restart, because every pod is blocked reading a config service that is itself waiting to be scheduled.
- • Blind incident: the operator sees dashboards go flat and cannot distinguish "traffic stopped" from "metrics pipeline stopped", because both traverse the same failing component.
- • Certificate cliff: the operator sees every service-to-service call fail simultaneously with TLS errors, with no deploy and no traffic change to explain it.
- • Analysis without action: the operator finds a two-year-old document that correctly predicted the exact outage now in progress, with no owner and no follow-up recorded.
- • Building the graph is cross-team coordination and that is where the value is: the missing edges live precisely at team boundaries.
- • Shrinking a radius usually requires splitting something shared, which converts a coordination-free resource into several, and often costs efficiency to buy independence.
- • No coordination is required at failure time — blast radius is analysis, not mechanism. Its output feeds mechanisms that must themselves work without coordination.
- • Accepting a radius requires a named owner with authority to accept the risk, which is a coordination act with a person, not a system.
- • Components outside the radius continue to provide their normal guarantees, provided the radius was computed correctly — which is the assumption under test.
- • Components inside the radius provide whatever their degradation policy defines, and nothing if none is defined.
- • A control-plane failure leaves the data plane intact but frozen: current guarantees hold, and no change to them is possible.
- • If the observability path is inside the radius, you retain the guarantees and lose the ability to confirm them.
- • Detect: co-failure of components with no logical dependency is the signature of an undrawn edge — treat it as the primary clue rather than a coincidence.
- • Contain: isolate the shared resource if you can — separate the pool, drain the node, pin the affected tier — before diagnosing.
- • Recover: restore the shared component, then restart dependents in dependency order, because startup dependencies bite hardest here.
- • Reconcile: update the graph with the edge you just discovered, as an artefact, not as a postmortem sentence.
- • Verify: re-run the deletion analysis for the repaired component and confirm the new predicted radius matches what you observed.
- • Co-failure correlation across components with no call path — the empirical way to find undrawn edges without a workshop.
- • A service graph derived from actual traffic or traces rather than from documentation, refreshed continuously.
- • Startup-time dependency reads, logged explicitly, so boot-only couplings are visible outside a fleet restart.
- • Whether the metrics and alerting path shares any component with the services it monitors.
- • Certificate and credential expiry dates as a monitored signal — a scheduled, entirely predictable total outage.
- • Before an availability commitment: you cannot promise a number without knowing what single failure invalidates it.
- • After any surprise co-failure, as the direct follow-up: the incident just handed you a missing edge for free.
- • When choosing where to spend redundancy budget, since it ranks components by consequence rather than by intuition.
- • On a small system with six components and one database, where the answer is "everything depends on the database" and the exercise is ceremony.
- • When it produces a diagram instead of decisions — the artefact then costs maintenance and buys nothing.
- • When treated as an upper bound, which encourages confident statements about what cannot be affected.
- • Derive the graph from distributed traces instead of interviews: it is automatic, always current, and it captures calls nobody documented — though it still misses shared-fate infrastructure.
- • Run the deletion experimentally rather than on paper for the cheap cases; the observed radius beats the predicted one.
- • Fault-domain analysis by hosting boundary (zone, cell, account) as a cheaper proxy when the service graph is too large to walk node by node.
- • For small systems, a single question — "what is the one component whose loss stops everything?" — captures most of the value at a fraction of the cost.
Click a component. What dies with it?
What people believe, and what is true
The architecture diagram shows the blast radius.
It shows calls. Failures also propagate through shared hosts, pools, control planes, name resolution and certificates, none of which are call edges.
This service is not a runtime dependency — it is only read at startup.
Then it is a total dependency for every fleet restart, which is precisely what happens during recovery from an unrelated incident.
The control plane being down is not urgent because everything is still serving.
It means you cannot deploy, scale, fail over or rotate anything. It is not urgent until the moment you need to act, and then it is the whole incident.
We know what the blast radius is.
You know a lower bound. Every surprise co-failure in your incident history is a counterexample to a previous confident answer.
Go deeper
Only the levels this lesson can honestly fill — a missing level is a claim nobody had.
Overview
For each node in the dependency graph, ask what stops working if it dies. Write the answer as user-visible features. Expect it to be worse than you think.
Practical
Build the graph from traces, then add the edges traces cannot see: shared pools and hosts, startup-only reads, DNS, certificates, IAM, and the observability path. Rank by impact times fragility. For each oversized radius pick shrink, contain, degrade or accept — and record the owner.
Advanced
The useful abstraction is fault domains rather than services: partition components by what they share fate with, and the radius of any failure is the domain that contains it. This reframes redundancy as a question about whether your replicas are in different domains — three replicas on one node pool are one replica with extra cost, and the service graph will never tell you that.
Apply it
- 🔧 Pick a component your team considers non-critical and trace every consumer, including startup reads. Report the widest user-visible consequence you find.
- 🔧 List every component on the path between a request failing and an engineer being paged. That path is your observability blast radius.
- ⚡ A rolling restart of an unrelated service triggers a fleet-wide restart, and nothing comes back for eleven minutes. What class of dependency explains this, and how would you have found it in advance?
- 💬 Pick the least important service in your architecture. What stops working if it disappears for an hour?
- 💬 Two services with no call path between them failed at the same moment. What kinds of coupling would explain that?
- 💬 Your service reads its configuration only at startup. Is the config service a dependency? When?
- 💬 Your metrics pipeline runs on the same cluster as the services it monitors. What does that cost you?