Blog

AI didn't make engineering discipline obsolete. It made it the whole job.

Tests, small PRs, readable code, documentation: every engineering best practice just got more valuable, because every one of them is about managing change, and change is what AI multiplied. But one practice has no guardian at all, and it's the one that decides whether your codebase survives the next two years.

There’s a story making the rounds in engineering circles, and I understand why it’s comforting: AI writes cleaner code than most humans, so the old disciplines matter less now. Fewer reviews. Lighter process. Let the tools carry it.

I think that story has it exactly backwards, and I want to walk through why, starting not with architecture but with the humble stuff: tests, naming, small pull requests. The things every senior engineer preaches and every deadline erodes.

Best practices were never about writing code

Here’s the thing about engineering best practices that gets forgotten: almost none of them exist to help you write code. They exist to help you change code later, safely, without holding the whole system in your head.

Think about what each one actually buys:

What each practice is really for

1

Tests

Not proof of correctness. Confidence to change something six months from now without fear.

2

Readable code & naming

Cheap onboarding for the next person, who is usually you, later, with no memory of why.

3

Small PRs

Units of change a human can actually hold in their head and meaningfully judge.

4

Docs & ADRs

Shared memory. The decisions that outlive the people and conversations that made them.

The common denominator: every practice manages the cost and risk of future change. None of them is about typing speed.

Now ask: what did AI coding tools actually change? Not correctness per line, not fundamentally. What they changed is volume of change. Teams that shipped five PRs a day ship fifteen. Refactors that would have been postponed forever now happen in an afternoon, because generating the code is no longer the expensive part.

If best practices are the machinery for managing change, and AI just multiplied change, then every one of those practices became more load-bearing, not less. Tests matter more because more code lands between human readings. Naming matters more because more code is read by people who didn’t write it, including the models generating the next change on top of it. Docs matter more because they’re now consumed by agents as well as people.

The data backs this up, and it’s not subtle:

What the industry data shows

8x
increase in duplicated code blocks in 2024 vs. two years prior (GitClear, 211M changed lines)
-7.2%
delivery stability per 25% increase in AI adoption (Google DORA 2024)
46%
of code in Copilot-enabled files is AI-written (GitHub research)
2024
the first year copy-pasted code exceeded refactored code, in GitClear's corpus

Sources: GitClear AI Code Quality research, Google's 2024 DORA report, and GitHub Copilot research. Every figure here is somebody else's; we have not run a study of our own on this and are not going to invent one.

GitClear’s number is the one I keep coming back to. Across 211 million changed lines, 2024 was the first year that copy-pasted code exceeded refactored code. Duplication rising in lockstep with AI assistance. That isn’t a story about bad code. It’s a story about system-level properties degrading while everyone’s attention stays at the line level.

The practices that scale themselves, and the one that doesn’t

Here’s where it gets interesting. Not all best practices are equally at risk, because not all of them depend on a human paying attention.

Most of the classics have a guardian that scales automatically. Style has linters. Correctness has tests and CI. Even readability has help now, since coding assistants are genuinely good at naming and idiom. Crank the volume to 10x, and these hold the line, because the enforcement is mechanical and per-file.

But look at what’s left unguarded:

Every practice has a guardian. Except one.

PracticeWho enforces itHolds at 10x volume?
Consistent style & formattingLinters, formatters✓ Automatic
CorrectnessTests, CI, type systems✓ Automatic
Readable code, good namesReview norms + coding assistants✓ Mostly
Small, focused diffsTeam norms✓ If you insist
What your docs say about the codeNobody✗ Goes stale silently
Dependency direction & boundariesNobody✗ Erodes silently
Modules keeping their internals privateNobody✗ Erodes silently
No dependency cyclesNobody✗ Erodes silently

The unguarded rows share a property: they're global. You cannot check them by looking at one file, one diff, or one PR. They exist only in the relationships between components, which is exactly what per-file tooling can't see.

This is the transition the comforting story misses. The practices that survived the volume increase are the local ones. The practice with no guardian is architecture: what your docs say the system is, which component depends on which, whether boundaries hold, whether the shape is drifting. And architecture is the practice where damage compounds hardest, because you can’t refactor your way out of a shape problem one file at a time.

Why architecture decays at exactly AI speed

Before AI tools, there was an accidental safety mechanism nobody designed: the speed of human typing. Code changed about as fast as a senior engineer could keep a mental model of it. Reading diffs was a workable proxy for “is the system still healthy?” because the system changed slowly enough for a head to track.

Output scaled. Oversight didn't.

Code written, before AI
1x
Code written, with AI
3-10x
Architectural review capacity, before
1x
Architectural review capacity, now
1x

The bottleneck on writing disappeared. The bottleneck on noticing what the writing did to the system is still one human head, reading diffs.

That proxy is now broken, and here’s the mechanism. A diff shows you lines. It does not show you that those lines made a sentence in your own README false, or created the first-ever edge from your core into a plugin, or closed a cycle across five packages. That information lives in the relationship between this change and everything around it: other files, other documents, every change before it. It is structurally absent from the thing your reviewers are reading. Here is a real one:

The same pull request, two representations

What review sees multithreads/NodeWorker.java · +9 −150 − import …repair.scheduler.RepairScheduler; − private final RepairScheduler myRepairScheduler; + private final SchemaRefresher mySchemaRefresher; + mySchemaRefresher.onTableCreated(myNode, tableEvent); ✓ Review approved ✓ Merged A clean refactor. Nothing to flag. What the README still says core.impl/README.md · not in the diff 133 NodeWorker — A continuously-running background thread… 134 - Discovers all replicated tables for its node 135 - Fetches repair configurations per table 136 - Calls RepairScheduler.putConfigurations() to keep jobs up to date 137 - Loops on a configurable refresh interval ✗ Violated: NodeWorker depends on RepairScheduler True at the base revision, false after this change

Ericsson/ecchronos #1786, a real pull request. The call moved to a new class, SchemaRefresher; the sentence on the right did not move with it. It sits in a file the diff does not contain, so no amount of careful diff-reading surfaces it, and it is still on master. The whole story.

This is the pattern that shows up when you go and look. Across thirty recent open-source pull requests we analysed, most moved nothing structural at all — and a handful did something a reviewer would want to know about and could not have seen: a public interface losing a method that twelve components depend on, a component reaching into two packages it had never touched, a README describing an API that no longer exists. Every one of those pull requests was reviewed by people who are good at their jobs. The information simply was not in front of them.

The bill comes due quietly

The failure mode of high-volume AI development isn’t dramatic. Nothing crashes. The failure mode is a codebase that accumulates coupling, cycles, and misplaced responsibilities one clean-looking PR at a time, until one day the symptoms surface as things nobody connects back to architecture: builds got slow. Onboarding takes months. Every estimate has a fudge factor because every change touches more than it should.

Teams that ship 10x faster while their structural oversight stays flat aren't being efficient. They're borrowing. The loan comes due as a system that technically passes every check while becoming harder to change every week, and by the time it's obvious, the cheap moment to fix it is hundreds of merges in the past.

So what do you actually do?

Keep every practice you already have. They matter more now, not less; that’s the whole first half of this post. But be honest about the gap: nothing in your current setup is watching the graph.

Closing that gap doesn’t mean hiring architects to trace dependencies by hand, and it certainly doesn’t mean slowing your team down to pre-AI speed. It means giving the one unguarded practice the same thing every other practice already has: an automatic, per-PR guardian.

That’s what Striff is. It reads the architecture your docs already describe, turns every checkable sentence into a rule, and evaluates each one at both revisions of every pull request. Alongside the rules, fourteen structural checks speak up only when something happened that the diff cannot show: a first-ever edge between two packages, a cycle closing, a reach into another module’s internals, a public contract shrinking under things that depend on it. On the rest it reports clean and tells you what it looked at.

The bar is deliberately high, and the consequence is that it is quiet. That is the same bargain your linter makes: you trust it because it does not shout. Your linter guards style, your CI guards correctness, and what you wrote down about the shape of the system finally gets a guardian of its own, at whatever speed your team ships.

Install it on a repository and open your next pull request.