Blog

1 in 3 refactor PRs quietly moves the architecture. Nobody's review mentioned it.

We analyzed merged refactor PRs from well-known repos like Presto, Google Cloud DataflowTemplates, Apache Beam, and LINE armeria. A third carried real structural risk: boundary inversions, package cycles, contract rewrites. Here's what that risk looks like, on real Striff diagrams.

Here’s a number worth sitting with: when we analyzed merged refactor-shaped pull requests from popular open-source repos, 1 in 3 changed the architecture in a way that deserved a reviewer’s attention. Boundary inversions. New package cycles. Contracts with dozens of dependents rewritten in passing.

All of these PRs were reviewed. Many had AI review bots active on the very PR. All of them merged anyway, because the risk wasn’t in the lines of the diff. It was in what the change did to the dependency graph, and no one was looking there.

The experiment

2,879
recent merged PRs surveyed across popular Java, TypeScript, Python, and C# repos
91
refactor-shaped candidates fully analyzed by Striff, end to end
1 in 3
carried HIGH or MEDIUM structural risk that no review mentioned
2 in 3
were structurally clean. Striff posted nothing on those, by design.

Method: merged PRs from April–July 2026 with structural-change titles (refactor, extract, move, split…) and 2–30 changed files, analyzed with the same public Striff pipeline the browser extension uses. Every repo had an AI review bot commenting on the analyzed PR, and none of the findings below appear in those comments. Of 27 flagged PRs we showcase the strongest; we discarded several whose findings were test-module artifacts, because a finding that makes you shrug is a bug in our book.

If you lead a team, the second number matters as much as the first. A structural reviewer that flags every PR is a noise generator you’ll learn to ignore. Striff stayed silent on two thirds of these PRs because there was nothing structural to say. The third where it spoke up is the third that compounds into slow builds, scary refactors, and the rewrite nobody budgeted for.

What the risk actually looks like

These are real, merged pull requests, shown exactly as Striff rendered them. Each diagram is zoomed to the finding.

prestodb/presto #28184: four load-bearing contracts modified in one PR

prestodb/presto · PR #28184 Striff structural diagram of Presto PR 28184, zoomed on HiveUtil and HiveTableLayoutHandle
HIGH
Stable contracts modified: 35, 28, 22, and 16 dependents. HiveUtil, ParquetTypeUtils, Field, and HiveTableLayoutHandle all changed in a single PR. Every dependent of those components inherited the risk, and the diff gave no hint of the blast radius.

This is what "a small change to a shared component" looks like when you can actually see the dependents. Reviewers approved the lines. The graph shows what the lines couldn't: see the PR.

floci-io/floci #1825: a package cycle is born

floci-io/floci · PR #1825 Striff structural diagram of floci PR 1825, zoomed on the CloudFormation provisioner cycle
HIGH
New package-level dependency cycle through the CloudFormation services, plus two first-ever boundary crossings. From this PR onward, those packages build, test, and break together.

Cycles never announce themselves; they arrive as one convenient import. Why one edge is all it takes, and the PR itself.

intro-skipper #831: two layers reach straight into the database

intro-skipper/intro-skipper · PR #831 Striff structural diagram of intro-skipper PR 831, zoomed on the Analyzers to Db boundary crossing
HIGH
First-ever edges from Analyzers and Manager into Db, each skipping two layers. The layering that kept analysis logic away from persistence is now optional, and every future PR can cite this one as precedent.

Layer skips are how architectures erode: each one makes the next easier to justify. See the PR.

The full flagged set spans repos your team probably knows: Google Cloud DataflowTemplates accumulated coupling across 15 components in one PR, apache/beam grew a 24-dependent component’s reach, LINE’s armeria piled complexity onto its xDS plugin path, and oshi watched one class jump from complexity 71 to 111 in a single merge.

The shape of the problem

The 64 findings, by type

Coupling / instability spikes
20
Complexity (WMC) growth
20
First-ever boundary crossings
13
Stable-contract changes
6
Layer skips
4
New package cycles
1

Every category is invisible in a diff by construction. Coupling deltas require the before-and-after graph. "First-ever edge" requires repo history. Cycles require paths through files the PR never touched.

Notice the mix. It isn’t dominated by one exotic detector. It’s the everyday mechanics of a codebase getting worse: components quietly gaining dependencies, complexity pooling in hubs, boundaries crossed for the first time. Individually, each one merges without a ripple. Together, at a rate of one risky refactor in three, they are the reason a codebase that felt fast two years ago feels slow today.

What this means for your team

If your team merges ten refactor-ish PRs a week, this data says roughly three of them are moving your architecture, and the odds that anyone notices in review are low. Not because your reviewers are careless, but because the information isn’t in front of them. Blast radius, edge direction, and cycle paths live in the dependency graph, and nobody builds the dependency graph during code review.

Striff builds it on every pull request. It parses both sides of the PR into a component graph, compares them, and posts findings only when the structure actually moved: which contract got riskier, which boundary got crossed, which cycle closed, with the numbers to back each claim. On clean PRs it says nothing at all. Your team keeps its review habits; the graph just stops being invisible.

Every PR in this post is public. Click through, read the reviews, and check our work. Then install the browser extension and open your own next pull request. If you’re in the two thirds, you’ll hear nothing. That’s the point.