A pull request lands. Two approvals, tests green, CI blue. It ships on a Tuesday.
Three weeks later there's an incident. Somebody opens the file and finds a branch nobody remembers discussing. It handles a rare case, and handles it wrong. You ask the author what it's for. They squint at it. They wrote it. They can't tell you why.
This isn't a story about a bad engineer. Every engineer I know has been that author, including me. The code I understand least in anything I ship is the code I generated fastest, and it is almost always the code furthest from the domain I'm strongest in. On my home turf a bad suggestion smells wrong immediately. Two directories over, it just looks like code.
So we get faster and we understand less, and the two facts don't feel connected because they show up in different places: velocity on a dashboard, confusion in a postmortem, six weeks apart.
They're connected.
We have a word for code we understood and shipped anyway. Technical debt. It's a good word precisely because it names a decision: someone weighed the shortcut, took it, and, in the good case, wrote down what it would cost to unwind. Debt implies a lender, a balance, and a plan. You can put it on a roadmap. You can argue about it with a straight face.
What we don't have a word for is code that entered the system without ever passing through anyone's understanding.
Call it comprehension debt. It's the gap between what your codebase does and what your team can explain. The difference that matters: technical debt is a liability you chose, and comprehension debt is a liability you acquired. Nobody decided. There's no ticket, no comment, no // TODO: this is a hack because
. The commit looks exactly like the commits around it. That's the whole problem: it is invisible at the moment it's created and expensive at the moment it's discovered, and those two moments are far enough apart that nobody connects them.
It's also invisible to every metric on your squad's dashboard. Velocity can't see it. Deployment frequency can't see it. Coverage especially can't see it - generated tests will happily assert the behavior of generated code, and the pair will be wrong together, forever, in perfect agreement. If your dashboard measured comprehension debt, you'd have noticed it accumulating a year ago. It doesn't, so you'll notice it during an incident. Nobody's bonus depends on comprehension.
That's the vocabulary. Here's the mechanism.
The common version of this complaint is that AI writes bad code. That's wrong, and being wrong makes it useless as an argument. You say "AI writes bad code," someone shows you a clean, idiomatic, well-factored function, and the conversation is over. You lost, and you were right about something, but not that.
The vendors say superpowers. The critics say slop. Both are talking about the code, and the code was never the problem.
Here's what happened.
Building software used to be a sequence. Not a waterfall, but a sequence with real steps in it. You figured out the problem. You argued about what "done" meant. You sketched a design and someone senior poked holes in it. You wrote down what you'd accept. Then you built it. Some of those steps took ten minutes and lived on a whiteboard. Fine. They still happened, and each one left something behind: a decision, a constraint, a written-down definition of done. The residue of those steps was understanding, distributed across the people who took them.
AI collapsed that sequence into one step. Prompt in, implementation out.
We have a name for the one-step version now. Vibe coding. It's an honest name, which I appreciate - it tells you exactly how much of the process survived.
And notice what got skipped. Not the coding. The coding is the one part that got faster. What got skipped is everything upstream of it: the problem framing, the design argument, the acceptance criteria. Those were never in the code, so a model trained on code never learned they existed. You are asking for the output of step five while providing the input to step one, and the model does not object. It cannot know that steps two through four are missing. It has never objected to anything. It has no memory of why. It was never shown a why. It was shown a hundred million whats.
The insidious part is the asymmetry of the evidence. A skipped design conversation produces no artifact. There is no file in the repo called the-argument-we-didnt-have.md
. So the absence leaves no trace inside the codebase: the diff looks the same whether the design was debated for an hour or never considered at all. The only difference is in someone's head, and if the step was skipped, it isn't in anyone's head either.
The obvious answer is that we should think before we type. Everybody already knows this. I know it. Early in my career I sat in a code review where someone made me defend a design for the better part of an hour, and I remember that design today. I remember very little else from that job.
I still skip the step. Not always. But more than I did two years ago, and the reason is structural.
The environment offers no resistance. Skipping the design conversation has no immediate cost and an immediate benefit: the PR is up, the ticket moves, the dashboard is green, and the cost arrives in six weeks disguised as an unrelated incident. Any behavior with an instant reward and a deferred, misattributed penalty will win against your intentions. That's not a moral failure. It's a feedback loop, and you don't fix feedback loops with willpower. You fix them by changing what the environment permits.
Nobody measuring you has ever asked whether you understood it. They ask how many.
That's the only interesting question here: what would it look like if the steps couldn't be skipped?
I'm part of the team that's been building an answer, so read the rest with that bias in mind: I helped build the tool, and I'm the person least qualified to tell you it works.
The tool is called nWave. It runs inside Claude Code - same harness you already have, no new thing to adopt. The idea is simple: put the sequence back, name the steps, and make each one produce an artifact you have to look at.
Seven waves. Discover the problem. Diverge on approaches. Discuss until there are user stories and acceptance criteria. Design the architecture. Plan the delivery path. Distill the acceptance tests. Deliver the code, test-first. Only the last two are mandatory: every feature ends with acceptance tests and test-driven code, and the five upstream waves are optional, because a bug fix doesn't need market discovery and pretending otherwise is how methodologies die.
And when you genuinely don't know whether a mechanism will work, you timebox a probe and find out before you design around it. The answer is a finding, which is an artifact like any other.
Three things about this matter more than the wave list, which is the least interesting part.
Each wave leaves an artifact. This is the actual mechanism. The reason a skipped step is invisible is that it produces nothing; the fix is to make every step produce something. A user story. A design document. A Given-When-Then scenario. Now the absence is visible, because the file is either there or it isn't, and you can review a file.
The gates are deterministic. Not an LLM grading an LLM, which is a lovely way to generate confident nonsense at scale. The checks are hooks: code that inspects the artifacts and blocks or doesn't. A test that fails for the wrong reason doesn't count as red. A commit without a test doesn't pass. The machine's opinion is not consulted.
Nothing runs unsupervised end to end. Each wave stops and hands you its output. You approve, or you don't. It is, by design, slightly annoying.
Here is the objection I'd raise if I were reading this, and I'd raise it hard: you just described a system that orchestrates AI agents through the entire lifecycle, and you're pitching it as a defense against over-delegating to AI. That sounds like an arsonist selling smoke detectors.
The distinction I'd argue for is where the human is required versus merely permitted.
Today you're permitted to review AI output. You're permitted to think about the design first. Permission is not a control. What actually happens is that permission decays into habit, and habit decays toward whatever is cheapest.
The waves exist to force decisions to surface at the points where they're cheap to make and expensive to skip: what are we building, what does done mean, what shape should this take, what will we accept. Those are judgment calls, and nothing in the system can make them for you; the AI can draft, propose, argue, and generate, but it cannot proceed past a step you haven't ratified. That's the whole design. Not autonomy. Checkpoints.
Whether it delivers on that is a separate question, and one I can't answer for you from inside the tool I helped build.
It's slower. Per feature, unavoidably slower, and anyone who tells you their methodology is free is selling you something. You're paying, in wall-clock time and in tokens, for steps you were previously skipping for free. That's the entire trade: you move the cost from the incident, where it's expensive and unpredictable and hits you at 2am, to the design conversation, where it's cheap and scheduled. This is a good trade in exactly the situations where the incident is expensive.
Which means it's the wrong tool for a lot of work. A throwaway script, a personal project where you are the entire audience, anything nobody will ever have to explain: put the ceremony down and go fast. There's no comprehension debt if there's nothing to comprehend.
It earns its cost where "ship fast and fix later" runs into something that doesn't accept "later" as an answer. Payment rails. Anything with a compliance audit at the end of it. Anything where the edge case you didn't think about is somebody's money, or worse. If you work in one of those places you already know the feeling of shipping something you can't fully explain, and you already know it doesn't get better on its own.
I'm not going to tell you to adopt this. You've been doing this for years and you don't need a framework from a stranger on the internet. There are already several.
What I'd actually like is for you to open the repo and tell me where the methodology is wrong. The next post gets concrete: what a single wave looks like when you run it, and why the acceptance tests come from a different agent than the implementation.
sh -c "$(curl -fsSL https://raw.githubusercontent.com/nWave-ai/nWave/main/scripts/install/install.sh)"
Yes, that's a curl piped into a shell, and if your instinct is to read the script first rather than run it, good: that's the entire thesis of this post applied to me. It's here. It prefers uv
, falls back to pipx
, installs the CLI, then runs the installer. If you'd rather not pipe anything into a shell, pip install nwave-ai && nwave-ai install
reaches the same place. Reach for uv
if you can: it's the recommended path, and pip and pipx are supported fallbacks rather than equals.
The waves are markdown. The gates are Python. All of it is readable in an evening, which is deliberate: a tool that asks you to trust it more than you can inspect it has the same problem as the AI it's trying to discipline.
In the meantime, one question worth carrying into your next PR review: can the author explain why?
If the answer is no, the code shipped anyway. It always does. That's the part that should bother you.