You change one small shared module. Tests are green. The type-checker is happy. You merge, because everything you can see from inside that repository says the change is safe. Then a pipeline fails in a repo you had forgotten existed, owned by a team you have never spoken to, that pulls your module in through a line nobody remembers writing.
Every engineer who has worked in a multi-repo estate has lived some version of this. The canonical public version is left-pad: eleven lines of JavaScript, unpublished by one author in 2016 and used as a transitive dependency by everything from small projects to major companies including Facebook, Netflix, and Spotify. When it vanished, dependent builds across the ecosystem received 404s and failed, because almost nobody knew how many things quietly depended on it. Left-pad was an unpublish, an availability event rather than a breaking change, but the shape is identical: transitive consumers nobody had counted. The action was trivially safe from where its author stood. The damage happened everywhere else.
That gap has a name now, and the name is doing too much work. Blast radius is not one measurement. It is two, and most of the tooling that promises to measure it only sees the one that stays inside a single checkout.
The word is doing two jobs at once
"Blast radius" quietly conflates two different measurements: the code blast radius your tests, type-checker, and impact-analysis tools already catch inside one repository, and the artifact blast radius that crosses repository boundaries through base images, Terraform source blocks, and the like, which nothing in a single checkout can see.
These two live at different layers and fail in different ways. The code radius is the set of functions, files, and callers inside your repo that your change touches. It is real, and it is largely solved for the static edges. Your test suite runs it. Your language server draws it. A file-level import graph traverses it. If your change breaks something one import away, you find out before you merge.
The artifact radius is the set of other repositories, services, and pipelines wired to the thing you changed through a manifest edge that lives in someone else's repo. From your checkout, that edge is invisible. You cannot grep for it, because the file that references your module is not on your disk. The consuming repo declares the dependency, in its manifest, on its side of the boundary. This is the radius that pages you at 2am, and it is the one your CI never saw.
The reason this matters more every quarter is speed. AI coding agents now make small, plausible, locally-correct cross-repo changes faster than any human reviewer can trace them, and a 2026 Cortex benchmark of engineering leaders reported change failure rates up roughly 30% and incidents per pull request up 23.5% alongside that acceleration. Amazon ran an internal deep-dive in March 2026 into a trend of incidents with, as reported by the Financial Times, "high blast radius", tied in part to novel GenAI usage running ahead of established safeguards. The occasion is new. The blind spot is old.
What blast radius actually is
Blast radius is the total set of things that break, degrade, or need updating when a change ships, measured along a spectrum from the function you edited out to the repositories you have never checked out. It is a system property, not a code property, and the honest definition has to hold the whole spectrum at once.
At the narrow end sits the function: change a signature, and every caller in the file feels it. One step out is the module: the internal package that a dozen files import. One step further is the service: the API other services in your process boundary call. These three all live inside a repository, and inside a repository the graph is legible. You own the code, you own the tests, the edges are on your disk.
The spectrum does not stop at the repo wall. The widest band is the cross-repo artifact edge, where your change reaches a consumer through a construct that neither repository executes together: a Docker FROM line pinning your base image, a Terraform source resolving to your module, a Helm value feeding a downstream chart, a GitLab CI include pulling your pipeline template, a go.mod require on your published package. Same word, same question, radically different visibility. The industry mostly defines blast radius at the layer it can already see, and then stops.
That is the fragmentation you hit if you go looking for the definition today. LOOM's glossary scopes it to "how many functions, classes, and files are affected when a single code element changes". Axiom Refract defines it as the set of files that break when a given file changes, computed by BFS over a file-level import graph. Recursive pushes the other way, all the way out to business impact, features and customers and revenue. SixDegree reaches furthest, into infrastructure, data, and people. Each is correct at its layer. None of the widely-cited ones names the actual cross-repo manifest constructs as the edge. That is the gap this hub exists to fill.
How to measure it
You measure blast radius by building a directed dependency graph and traversing it backwards from the thing you changed, and every method in common use does exactly that up to the repository boundary and then stops. The differences between methods are real and worth walking honestly, because each one genuinely works where it works.
Dependency-graph BFS
The dominant mechanical method, and the one the 2026 tooling wave has converged on, is a breadth-first search over a dependency graph. Nodes are files or symbols, edges are imports or calls, and you traverse the reverse edges out from the changed node: depth 1 is the direct dependents, depth 3 to 5 is the transitive tail. LOOM, Axiom Refract, and Pharaoh all describe this mechanism precisely, and it is the right mechanism. It is deterministic, it is fast, and it answers "what imports this" without guessing.
Its boundary is the graph you built. A file-level or symbol-level import graph is scoped to one parsed codebase. It has no node for the repo across the org that pins your image, because that node was never in the graph. The BFS is only as wide as its inputs, and its inputs stop at the checkout.
Impact-based test selection
A sharper version runs the graph to pick which tests to execute: predict the affected code, run only those tests, skip the rest. When it works it is a genuine accelerator, and for a large single-repo test suite it is worth real money. It answers "which of my tests could this break" with precision.
It inherits the same wall, plus a second one. The affected tests it can select are the tests in the repository it is running in. A consumer in another repo has its own suite, on its own side of the boundary, and your pre-merge run never invokes it. Worse, static selection is demonstrably imprecise for dynamic dispatch and event-driven code even within one language: the runtime edges are exactly the ones static analysis cannot see.
Change-failure-rate, the lagging outcome
DORA's change failure rate is the most-cited blast-radius-adjacent metric, and it is a genuinely useful health number, but it measures something structurally different from the two above. DORA defines it as the ratio of deployments that require immediate intervention following a deployment, a rollback or a hotfix. It is an outcome, aggregated after the fact, across however many deploys you count.
That makes it a lagging measure, not a graph you can consult beforehand. It tells you how often your changes broke something last quarter. It does not tell you which repositories are structurally exposed to the change sitting in your pull request right now. It is a number to drive down, not a map to read before you merge. Conflating the two is a common and expensive mistake: a good CFR trend and a completely unmapped artifact layer coexist happily, right up until they do not. If you want the metric itself, we wrote a practical guide to measuring your change failure rate.
For fact density, none of this changes when you switch ecosystems. Within the open-telemetry GitHub org, 16 other repos declare a go.mod require on go.opentelemetry.io/otel, as of the org's most recent completed scan, 2026-06-28. That edge is a first-class cross-repo dependency, and no single repo's BFS, test selection, or CFR contains it.
The radius you can't see from one repo
The widest band, the cross-repo artifact edge, is invisible from any single checkout by definition, and mapping those edges across an entire estate is its own discipline, cross-repo dependency mapping, which the rest of this post depends on.
This is where the bus-factor problem and the artifact problem turn out to be the same problem. The shared module nobody remembers owning is exactly the one that walked out the door with the engineer who last touched it. The edge is declared in a manifest, so it is still there and still load-bearing, but the knowledge of why it exists left the building. A parsed graph does not care who remembers; the edge is in the file whether or not anyone can still explain it. This is the dependency bus-factor problem stated structurally.
The AI-agent version of this blindness is its own case, because an agent working from one repo's clone structurally cannot see the other side of these edges, which is why AI agents in particular miss the cross-repo radius and why the AI-agent blast radius has become its own conversation. But the agent is the occasion, not the subject. The engineer who owns the estate and holds the pager had this blind spot long before the agents arrived.
How to contain it
Containment means three different things depending on which layer you are standing on, and only one of them is about knowing the graph before the change ships. Getting the reader honestly to the third one means conceding the first two in full, because both are real and neither is what a dependency graph does.
The cloud sense: cell-based architecture and breach containment
If you type blast radius aws into a search box, you almost certainly want fault isolation, and that is a legitimate, mature discipline that has nothing to do with cross-repo dependency graphs. AWS's Well-Architected guidance is to build bulkhead, or cell-based, architectures so a failure is contained to a limited slice of the system: if a workload uses 10 cells to service 100 requests, a failure leaves 90% of requests unaffected. Adjacent to it is the security sense, the scope of what a compromised credential or breached IAM role can reach. Both are real, both are important, and Riftmap does none of it. Cell-based architecture, IAM scoping, and breach containment are a different discipline from mapping which repos consume your Terraform module, and pretending otherwise would be the exact kind of overclaim this blog exists not to make.
The change-impact sense: flags and canaries
The other honest sense of containment is exposure control after the code is deployed, and it works. Canary releases and feature flags reduce blast radius by limiting how much traffic meets a bad change. DeployHQ puts it plainly: "Reduced blast radius. A bug that ships to 5% of traffic affects 5% of users, not 100%." LaunchDarkly frames the same move as decoupling deploy from release, so code can sit dormant behind a flag and be exposed to a ring at a time. This is genuinely valuable and I would not ship anything risky without it.
It answers a different question, though. Flags and canaries control what percentage of traffic is exposed to a change. They say nothing about which repositories are structurally wired to the thing that changed. The two are complementary, not competing: a canary limits the fallout of a break you did not foresee, and a graph tells you which breaks to foresee in the first place. If you do not know a downstream repo consumes your module, no canary percentage will tell you it exists.
Structural containment: know the graph before the change ships
Structural containment is knowing the cross-repo graph before the change ships, so the artifact radius is a fact you read rather than an incident you discover. This is the layer the other two leave uncovered, and it is a parsing problem before it is anything else.
Here is what that looks like as a real number. Across the 242 GitHub repos in the cloudposse org, 147 other cloudposse repos declare a Terraform source reference to cloudposse/terraform-null-label, the org's own foundational naming-convention module, as of a scan completed 2026-07-07 and queried live 2026-07-08. That is 147 live source = edges into a single module, discoverable from no single one of those repos' checkouts. Be precise about what the number is and is not: it does not mean 147 repos would break tomorrow. It means 147 repos hold a live declared edge to that module, and whether any given consumer breaks depends on the next release and on whether that consumer is pinned. Here 138 are already on the latest tag and 9 are lagging, so the sharper containment statement is not "147 would break" but "147 consume this, and only 9 are not already on 0.25.0". terraform-null-label is unusually central even for its own org, so read 147 as this estate's top example, not a typical average. The point is not the size of the number. The point is that you cannot answer the question at all from inside any one of those repos, and the answer changes every time someone bumps a pin. We walked all 242 cloudposse repos in a separate scan writeup if you want the full picture.
This is measurable because the edges are declared. Riftmap parses them, deterministically, from what the manifests actually say rather than inferring them from names or embeddings, across an entire GitHub or GitLab organisation from one read-only token, with no per-repo YAML to maintain. The traversal is a plain breadth-first search over repo-to-repo edges in Postgres. The product's own information model already encodes the distinction this post is built on: the API endpoint that answers the question is GET /repositories/{repo_id}/impact, its docstring titles it "Transitive downstream blast radius (repo-level BFS)", and it says in as many words that it "does not trace specific files, jobs, or symbols within the source repo". The code radius is deliberately out of scope. The artifact radius is the whole job.
I want to be straight about two limits, because a sharp reader will raise both. First, a parsed graph is only as complete as what is declared. An HTTP call expressed in no manifest, a queue consumer wired by convention, a service discovered at runtime: none of that is in a source block, and a declared graph will not show it. That is a real boundary, and runtime discovery is a complementary axis, not a lesser one. SixDegree makes the reasoned case for the opposite priority, preferring discovered dependencies over declared ones when they conflict, and they are not wrong that runtime telemetry catches edges parsing cannot. The trade is honesty: a declared edge is kept honest by the build that already executes it, so it does not drift; a discovered edge needs the change already running to be seen. If the declared-versus-discovered distinction is where your interest is, we drew it out fully in declared, inferred, registered. Second, parsing declared cross-repo edges is not something only Riftmap can do. The open-source tool grafel traces Terraform and Kubernetes edges across repos as a local daemon, and it is genuinely good work. The line I would draw is scope and shape, not capability: an org-wide hosted graph across a dozen ecosystems, parsed from one token, is a different proposition from a daemon you run locally per machine. Neither claim needs the other to be false.
If your estate is bigger than you can hold in your head and you want to see the artifact radius before the next change ships rather than after it pages you, point Riftmap at your org and read your own graph before you merge.
The blast radius you can measure in CI is not the blast radius that pages you at 2am. One lives inside a repository, where your tests already run and the graph is legible. The other lives in the manifests of repos you have never opened, declared in files that are not on your disk, and it is the whole reason "it worked on my branch" and "it took down three teams" are both true at once. Measuring the first has been solved for years. Measuring the second is a parsing problem, and it is the one worth solving before you merge.
Questions engineers actually ask
What is blast radius in software engineering?
Blast radius is the total set of things that break, degrade, or need updating when a change ships, measured from the function you edited out to the repositories you have never checked out. It splits into two layers: the code blast radius inside one repo, which your tests and type-checker already catch, and the artifact blast radius across repos, which travels through base images, Terraform source blocks, Helm values, and CI includes and is invisible from a single checkout.
How do you measure the blast radius of a code change?
You build a dependency graph and traverse it backwards from the changed thing: depth 1 is direct dependents, deeper hops are the transitive tail. Inside one repo, a file or symbol import graph and impact-based test selection do this well. To measure the cross-repo radius you have to parse the declared manifest edges in every other repo, because the consuming edge lives in a repo that is not on your disk and no single-repo traversal contains it.
How do you contain or reduce blast radius?
There are three senses. Cloud fault isolation (cell-based architecture, IAM scoping) contains failures at the infrastructure layer. Flags and canaries contain exposure by limiting what percentage of traffic meets a change. Structural containment means knowing the cross-repo dependency graph before the change ships, so the artifact radius is a fact you read rather than an incident you discover. The three are complementary, and only the third is about the dependency graph.
What's the difference between blast radius in AWS and the blast radius of a code change?
"Blast radius" in an AWS context usually means fault or breach isolation: architecting cells so one failure or one compromised credential reaches a limited slice of the system. The blast radius of a code change is the set of code, services, and downstream repositories affected when that change ships. They share a word and an intuition but operate at different layers, and a tool for one does not do the other.
About Riftmap
Riftmap maps cross-repo dependencies across your entire GitLab or GitHub organisation — Terraform, Docker, CI templates, Helm, and more. One read-only token. No YAML to maintain.
Top comments (0)