The Anatomy of a Breaking Change
Somewhere in your codebase right now, a function signature is about to change. Three services depend on it. Nobody knows.
A breaking change rarely announces itself at the PR stage. It doesn’t fail the tests. It doesn’t trip the build. It ships. Then three days later, a service the author never touched gets a request it wasn’t built to handle, and someone’s on-call rotation gets paged at 2 AM.
Today Qodo is releasing cross repo review in beta. What makes this class of bug so persistent is worth understanding: how breaking changes travel through a distributed system, why they clear single-repo review, and what it takes for a review agent to reason across repo boundaries.
How breaking changes actually propagate
Consider a realistic scenario. backend-api exposes a resolveUser() function used across the system. A developer removes the optional includeInactive parameter: a clean-up, long overdue, passing every test in the repo. The PR looks fine. It ships.
What nobody saw at review time: frontend-app was passing includeInactive: true on line 61 of UserPicker.ts. It now breaks. payments-service used includeInactive in two separate locations. Both break. auth-service called resolveUser() but didn’t pass the parameter; it probably still works, but needs a closer look.
That’s three downstream consumers affected by one parameter removal. None of them were visible in the PR. All of them are now a production incident.
This is what a breaking change looks like in a microservices codebase. It doesn’t fail where it was introduced. It fails where it lands.
In a monolith, this kind of impact is at least discoverable: shared types, shared tests, a build system that can surface cross-module breakage. In a distributed architecture, services are decoupled by design. That decoupling is the feature, but it’s also what makes breaking changes so dangerous. The coupling you removed from the architecture didn’t disappear. It moved into your implicit API contracts, your shared schemas, your function signatures. Nobody’s watching those at review time.
The structural blindspot in PR-scoped review
Pull request review is typically scoped to a diff. That’s its whole surface area: what changed, in this repo, in this branch.
That constraint is fine for a lot of what code review does: style violations, logic errors, security vulnerabilities within a function. Those are findable from a diff, but breaking changes don’t live in diffs. They live in the relationship between what you changed and what depends on it.
A reviewer looking at the backend-api PR has no signal that a downstream service exists, let alone that it will break. Even a senior engineer who knows the system well is working from memory, from mental models that may be months out of date, covering a system that’s changed in ways they weren’t part of.
Most code review tools compound this by design. They analyze the diff, maybe with some surrounding context pulled from the same repo. They have no model of cross repo dependencies. They can’t reason about which services consume a given interface, which event schemas have active dependents, or which shared libraries propagate a change to a dozen downstream consumers.
This isn’t a gap you can close with a smarter model or better prompting. The information isn’t there. You cannot reason about cross-repo impact from a single-repo view.
What cross-repo review requires
For a review agent to catch the backend-api breakage before merge, it needs three things:
A scoped model of the downstream consumers
Scanning the entire org on every PR isn’t feasible, so the agent needs a way to narrow the search space to the repos that could actually be affected. Package manifests don’t provide this: they tell you a version dependency exists, not whether a specific function or endpoint is actually called. What’s needed is an explicit model of which repos depend on which, and what kind of dependency connects them, because the failure modes differ by type. A code dependency breaks when a function signature changes. A service dependency breaks when an endpoint removes a field. A data dependency breaks when a schema column gets renamed. A pipeline dependency breaks when a build artifact changes its output interface.
If the agent doesn’t know what kind of relationship it’s looking at, it doesn’t know what to check: scanning for signature changes won’t catch a schema rename, and checking endpoint shapes won’t find a broken CI artifact. The kind of dependency determines what the agent looks for.
Consumer repo access at review time, at scale
Most code review systems already clone and analyze the PR repo. Cross repo review changes the scope: for every PR, the system may also need to inspect several consumer repos on demand. That introduces real scale, latency, and infrastructure challenges that don’t exist in single repo review. And it’s not just about having access to those repos. It’s about having access to the right state of them. Repos are continuously changing. A snapshot from last week might miss three new call sites added on Monday.
That’s why when a PR opens in backend-api, the agent needs to read frontend-app, payments-service, and auth-service at their current state: not a cached version, not a summary, the actual code as it exists at that moment. A summary can tell you roughly what a service does, but it can’t tell you that UserPicker.ts on line 61 passes includeInactive: true, which is exactly the level of precision you need to know whether removing that parameter is safe. Breaking change detection is a question about two specific states: what the source repo is changing, and what the consumer repo currently does with it. Both have to be current, and both have to be reachable at review time, for the answer to mean anything.
Focused Analysis
Having current consumer code solves the currency problem. It introduces a harder one: scope. Code review is already a difficult exploration problem within a single repo. Add several consumer repos and the search space grows significantly. An agent without a focused analysis path can easily drift into internal implementation details, irrelevant context, or follow threads that don’t bear on the actual cross-repo risk and generating noise in the process.
The way to address this is to keep the analysis path narrow by design. Rather than treating cross-repo impact as an extension of the general PR review, the agent needs a dedicated focus: assess risk at the boundary between the changed repo and its consumers, look specifically at what changed in the exported interface, map that against what each consumer depends on, and ignore everything else. frontend-app passing includeInactive: true is a confirmed break. auth-service calling resolveUser() without the parameter is ambiguous. That distinction matters, which is why the output isn’t a binary flag but a classification: breaking, likely-breaking, or review-needed.
The relation type taxonomy matters
Not all cross-repo dependencies are the same, and the detection pattern has to match the dependency type.
Code dependencies cover shared libraries and modules. The agent looks for function signature changes, removed exported symbols, and renamed types that consumer repos reference directly. This is where the backend-api example lives: the changed surface is a function signature, the consumer impact is a call site.
Service dependencies cover APIs and event contracts. When a REST endpoint changes its request shape or removes a response field, consumers that call that endpoint break. The agent analyzes endpoint definitions in the source repo and cross-references them against the API clients in consumer repos.
Data dependencies cover schemas, databases, and feature flags. A field removal from a shared schema or a flag rename in a central config service can break consumers that depend on that field or flag being present.
Pipeline dependencies cover CI artifacts and GitOps manifests. When a shared build action changes its interface or a GitOps manifest changes an artifact reference, downstream pipelines that depend on those outputs break.
Each type requires a different analysis approach. Treating them all the same, or treating package manifests as a proxy for all of them, misses most of the actual impact surface.
Introducing Qodo Cross Repo Review
Qodo’s cross repo review gives development teams visibility past the PR boundary. When a developer opens a PR, the review agent automatically checks every registered consumer repo and surfaces impact findings directly on the PR before merge. What previously required manual cross-repo investigation, institutional knowledge about who owns which service, and sometimes a production incident to surface at all gets caught at the PR, in the same review flow developers already use. The class of breaking change described above, silent and cross-boundary, becomes visible before it ships.
The relationship map defines exactly which consumer repos the agent checks when a PR opens. Teams register their repo relationships in the Qodo portal, and that registration is what scopes the analysis. The agent doesn’t scan the org. It reads the map and checks the repos registered as downstream consumers of the changed code. If no relationship is registered for a repo, the agent doesn’t run. Zero noise for repos not in scope.

Qodo’s context engine makes consumer code accessible at review time. When backend-api opens a PR, the agent reads frontend-app, payments-service, and auth-service at their current state, not a cached snapshot, not a summary, the actual code as it stands at the moment of review.
A dedicated cross-repo agent handles the analysis, focused specifically on impact at the boundary between the changed repo and its registered consumers. It looks at what changed in the exported interface, maps that against what each consumer depends on, and posts cross repo findings directly on the PR. Each finding links to the affected line in the consumer repo at the exact commit reviewed, so the engineer sees not just that something will break but precisely where.
Here’s what the conflict from the backend-api example above looks like when it surfaced in a Qodo PR review.

The finding is tagged as a Cross-repo conflict and marked Action Required. The description explains exactly what happened: backend-api removed includeInactive from resolveUser() without a deprecation window, and two downstream consumers that pass includeInactive: true will fail on the first call after deploy.
The Evidence section links directly to the affected lines across repos: frontend-app/src/components/UserPicker.ts [L61] and payments-service/src/billing/user-lookup.ts [L14, L33]. The engineer doesn’t need to search. What would otherwise take cross-repo grep and a Slack thread to track down gets surfaced on the PR, before merge.
Conclusion
This category of bug is predictable. A change looks clean in one repo, clears every check available to it, and breaks something in another. That’s the default state of distributed systems today. Changes that look correct in one repo break things in another, and the review process has no way to see it because it’s scoped to the wrong boundary.
Cross-repo review doesn’t change how code review works. It changes what the agent can see when a PR opens: which repos are downstream, what they’re doing with the changed interface, and whether the change is safe to ship.