DEV Community

Aleks Verein
Aleks Verein

Posted on

How we broke the code-review bottleneck with CodeSpeak - and the opportunity it opened

For decades, understanding the code better than anyone was an engineer's most valuable skill. The models have that now - and that turns out to be good news. Here's how we moved human review from the diff to the spec, kept the deep dives for code that warrants them, and used what it freed up to take intellectual control of the whole system, not just the code.

There is far more code in the world than there was two years ago, and a fast-growing share of it was never typed by a human. The models got good enough that most teams now let them write - and, increasingly, trust what comes back. Which surfaces a question the industry keeps circling and never quite says out loud: does all of that code still need a person to review it?

The question we keep not quite asking

For now, the answer is yes - because the models still slip more often than a careful human does, especially at the volume they produce. There's simply so much code that checking it has become its own job.

But that gap is closing from both sides. The models keep getting better, and an AI reviewer on every pull request catches more than a tired human ever did. We run one; it's real leverage, and if you ship with agents and you don't, you should. Code quality, the thing everyone is anxious about today, is the kind of problem that gets solved over time.

So play it forward. Suppose it is solved - the models stop making more mistakes than we do, the reviewers close the rest of the gap, and bugs stop being the thing that keeps you up at night. Is human review finished then? Is there anything left for an engineer to hold?

What's left isn't bugs. It's understanding.

There is, and it was never really about bugs. It's understanding - knowing what the code does, why it's shaped the way it is, what breaks if you touch it. The name for that is intellectual control, the thing Dijkstra worried about back in 1972, when he reminded us a program still has to fit inside one head - "the strictly limited size of his own skull."

For most of software's history, that was the most valuable thing an engineer had. Peter Naur put it sharply in 1985: the real product of programming isn't the text, it's a theory of the system that lives in the programmers' minds - so much so that "the death of a program happens when the programmer team possessing its theory is dissolved," even while the binary keeps running. The person who held that theory - who could trace any behavior to its cause without looking it up - was the one a company paid the most and feared losing. Intellectual control of the code was the skill.

And it's the skill the models just learned. Point Claude at a file and it reads, explains, and traces it about as well as the engineer who's prized for doing so - and far faster. Code-level intellectual control, the scarce and expensive thing, is quietly becoming a commodity. If your value was that you understood the code better than the people around you, the floor is moving.

There's one catch, and it's the important one: the model understands the code in front of it brilliantly, but it doesn't keep that understanding. As Sean Goedecke puts it, "AI agents can't retain theories of the codebase. They have to build their theory from scratch every time." The model is a superb momentary reader and a hopeless long-term owner. The persistent theory of the system still has to live in a human.

So the question for engineers isn't whether they've been replaced. It's narrower and more useful: if the model now owns code-level understanding, where should a human's understanding go?

The level nobody held

Up. To the one level intellectual control almost never reached: the whole system.

Be honest about the old world. Individuals held their files, their service, their corner of the map. But the understanding of how the entire thing fit together - across services, across teams, across the years - usually sagged. It lived in nobody's head, or in one over-subscribed person's, or it rotted as people churned and walked out the door with their theory. We tolerated that gap because holding the whole system was a luxury almost nobody could afford: the human budget was already spent, line by line, on the code.

That's what changes. The models can hold the code now. Which frees the scarce human attention that used to go there for the thing we never had the budget for - taking intellectual control of the system. Not "understand less." Understand higher. The frontier moves from "I know this function cold" to "I know how this whole machine behaves" - the more valuable understanding it was always too expensive to have.

That's the opportunity. The rest of this is how we chase it in practice - concrete enough to try on a single module this week.

Review the spec, not the diff

It starts by dropping an assumption: that "understanding the code" means "reading the code." Simon Willison - who co-created the Django web framework - keeps a golden rule: he won't commit code he "couldn't explain exactly what it does to somebody else". We kept his rule and moved its unit. It's no longer "can you explain this line" (Claude can). It's "can a human still explain what this system does." That's the understanding we refuse to give up, and the diff is a terrible place to keep it: at a thousand lines a pull request, the reviewer drowns in detail and surfaces with no theory at all. As Willison notes, once the typing is free, "the natural bottleneck on all of this is how fast I can review the results".

So we review a different artifact: a .cs.md file spec - borrowed from a tool called CodeSpeak, and used deliberately backwards from how it was designed.

CodeSpeak, built by Andrey Breslav (the creator of Kotlin), is a spec-as-source-of-truth system: you write a plain-English .cs.md describing what the software should do, and codespeak build generates the code from it. The spec is what you maintain; the code is the output, about 5–10× longer than the spec that produced it. There's even a one-time codespeak takeover command for pointing it at an existing codebase and extracting a spec from the code - a door you walk through once on your way to living in spec-land.

We inverted it. We keep the code as the source of truth, and treat the .cs.md as a generated description of what the shipped code already does - regenerated from the code, checked against the code, never compiled back into it. We live permanently in the place takeover only visits. We borrow CodeSpeak's format; we never run its compiler. Reverse CodeSpeak, if you want a name for it: the same tool, turned 180°.

Here's the shape of it - a file that's a few hundred lines of types, reducers, and guards collapses into something a reviewer holds in one screen (an illustrative shape, not a specific file):

# CheckoutReducer
## Short description
Folds checkout actions into cart and order state. Pure; no I/O.
## Data
Reads `Cart`; writes `Order`. Money is integer minor units, never floats.
## Functional requirements
- `AddItem` is a no-op when the SKU is already in the cart.
- `Checkout` fails closed when `total` is missing - emits no `Order`.
- `ApplyCoupon` runs before tax; an expired coupon is ignored, not rejected.
Enter fullscreen mode Exit fullscreen mode

That's a few hundred lines of behavior in a dozen you can actually read. Our review step regenerates the spec for every changed file, then builds the reviewer's reading path from the specs, not the source - the rule is blunt: don't open a source file when a sibling spec exists. Instead of a thousand-line diff, you walk a few hundred lines of compressed meaning in dependency order: what each piece does, what contract it holds, what changed.

So two reviewers do two different jobs, and neither pretends to do the other's:

Two reviewers, two jobs: the AI reviewer reads the code and finds bugs; the human reviewer reads the generated .cs.md spec, builds understanding, and takes system-level intellectual control - with deep dives into the code when it warrants them.

The model reads the code for defects. The human reads the spec for understanding - and still flags bugs, architecture, and design as they go, understanding is just the part this piece is about. We hand the line-by-line control to the thing that's now better at it, and spend the human on the level the model can't keep.

And there's a third reader, a side effect we were counting on: the agents themselves. Working in this codebase, they reach for the .cs.md before the source - the same compressed map the human reviewer walks, at a fraction of the tokens. It doesn't solve Goedecke's problem - the agent still rebuilds its theory every session - but it makes the rebuild cheap: a dozen lines of spec instead of a few hundred lines of code, for every file the task touches.

Keeping the map up

There's an obvious objection, and it's the right one: a map that nothing compiles, imports, or tests will quietly start to lie. A refactor moves the territory and the map says nothing. A description of code that's drifted from the code is worse than no description at all.

So most of the work isn't generating specs - it's keeping them up to date, without a compiler to lean on.

Two things do that. First, a deterministic gate on every change - a small, dependency-free script that runs on the developer's machine, inside the agent pipeline, and in CI on every pull request, and hard-fails the change on any of three findings:

  • The fingerprint check. Every generated spec ends with a stamped footer recording a hash of the exact source bytes it was generated from. The gate re-hashes the current source: if the code changed and the map didn't - one character is enough - the hashes disagree and the change fails on the spot. No string matching, no judgment call; the file either is the one the spec describes or it isn't. It's the closest thing the map gets to a compiler: not an opinion about drift, a proof of it.
  • The coverage check. Drift isn't only a map that lies - it's also territory with no map. A changed file that warrants a spec but doesn't have one fails the same gate. So does the mirror image: a spec whose source file no longer exists fails as an orphan, so deleted code can't leave a ghost map behind.
  • The removed-shape check. When a type or branch that existed before the change is gone from the source but the spec still names it, the gate fails and says which name - the fingerprint tells you that the map went stale; this tells you what lie it would have told.

None of those failures is something an AI reviewer can wave through - a deterministic signal overrides every softer opinion in the pipeline - so catching a stale map never depends on a human noticing it. A semantic pass behind the gate reads for the subtler drift a checksum can't catch: wording that's now misleading, a claim the code no longer keeps.

Second, regeneration is anchored: we keep the previous wording wherever behavior is unchanged and re-describe only what actually moved - so specs don't churn on every refactor, and when the spec and the code disagree, the code always wins.

And we still read the code. The spec is the default review surface, not a cage. When a change is gnarly, security-sensitive, or just unfamiliar, we drop into the actual source and do a deep dive. That's the point, not an exception to it: we ceded the cheap, line-by-line reading so we could afford to spend real attention exactly where a human is worth it.

Some files don't get a spec at all - and that's the same principle, not a gap in it. A presentational component's meaning is how it looks; a paragraph describing its spacing and color would be a worse map than the component itself. So we don't spec those - we look at them, in browser QA. Only the big, logic-heavy containers - real state, real branching, real gating - earn a .cs.md, and even then it's stripped of visuals: the contract, not the CSS. Spec what you'd reason about; look at what you'd eyeball. The whole point is to put each kind of understanding where it actually lives.

A note on the machinery, to head off the obvious question of who runs all this: the specs are auto-generated, never hand-written. They're refreshed as the work happens: when an agent edits a file, it regenerates that file's spec before it commits. And they feed a gate a human still owns - a person approves the merge.

There's a deliberate cost choice underneath that. A spec system that regenerates on every change is the natural thing to run against a metered, per-token model API - every regeneration is tokens you pay for, and the bill grows with your codebase. We wanted the opposite: a way to run the same approach on a subscription. So we generate the specs as a skill inside Claude Code, on the flat subscription our engineers already work in, not a metered per-token API. Regenerating a spec on every change is cheap by design - you can run it on the agent tooling you already have, with no new vendor and no per-token meter.

If you want to try it, the bootstrap is smaller than it sounds - ours was three steps, no new tooling:

  1. Point your agent at CodeSpeak and ask it to build the generator. That's not a simplification - it's what we did. We pointed Claude at the CodeSpeak docs and asked it to write a skill that produces a .cs.md in that format from a source file. The format is documented plain English, and "describe this file in this shape" is exactly the kind of task the model nails on the first pass. Alternatively, run CodeSpeak's own codespeak takeover on your codebase - fully compliant specs, straight from the tool that defined the format.
  2. Wire the skill into your dev pipeline. Regenerate the spec for every file an agent edits before it commits, remember to anchor the new file with the previous one, and add the drift gate - the fingerprint alone (hash the source bytes into the spec's footer, re-hash on every change) is a small script and catches most of the rot.
  3. Start narrow. Your stack doesn't matter - the spec is plain English, and plain English describes any codebase. Generate specs for your highest-churn module, fail the build when one drifts, and review the next pull request from the specs instead of the diff. One module is enough to feel whether you're holding more of the system or less.

The next altitude

Today our specs are per-file: one .cs.md next to each source file. That already lets a reviewer hold a whole change in their head at a level the raw diff never allowed. But a file is still a low rung, and "intellectual control of the system" is a promise a stack of per-file specs doesn't quite keep on its own.

So our next experiment - and it is an experiment, not something we've shipped - is specs a level up: for modules and subsystems, a higher-altitude map than the per-file ones. The goal is the full-system control that always used to sag: a human who can hold what the whole thing does, not just what each file does, and who reads down into the detail only where it earns the attention.

Notice what that is and isn't. It is not less control - it's the same scarce attention, re-spent and aimed higher. A reviewer working from good specs understands more system per unit of attention than they did back when they read every line - which makes this, oddly, more of a grip on the whole than the all-human world ever managed, not a retreat from it. That's the real deal on offer, if you take it on purpose: not "stop understanding your software," but "stop spending your understanding on the parts a model now holds, and spend it on the whole that no model will."

Trust is the side effect

We didn't build any of this to manufacture trust - and, honestly, not even for the big reason this whole piece is about. We built it because code review had become a bottleneck and we wanted reviews to go faster. Taking intellectual control of the whole system, and the trust that came with it, were what we found once we had it - not the plan.

In a June 2026 essay, Kent Beck named the dynamic better than we could. "We're accumulating code faster than we are accumulating trust", he wrote - and the reason it matters is that "software is bipedal-code & trust go together. One without the other just hops along awkwardly." Trust, he notes, "accumulates slowly & evaporates in an instant." A bug you can usually repair; trust, in his telling, is "irreversible."

You can't bolt that kind of trust on at the end. It's the residue of understanding. Keep a real grip on what the system does and trust comes along for free; lose the grip and no amount of green checkmarks buys it back. And it's concrete, not a mood: the reviewer who can actually say what a change does; the engineer six months from now who inherits the system and finds a current map of it, not a codebase whose theory left with whoever wrote it; the team that can hand work to an agent and still answer for what it built. We kept our grip on the thing that still needs a human - the whole system - and trust came as the side effect. Willison's rule survives intact: don't ship what you can't explain. We just moved where the explaining happens. Let the models have the code. Take the system for yourself.

Top comments (0)