Why "AI writes code" is the least interesting part of the shift
Code generation is the visible half of the change and the settled half. The unsettled half is everything the act of writing code used to hold together on its way past.
Every conference talk about this subject opens with the same slide. Someone types a sentence, a function appears, the room makes a noise. Then the talk spends the next thirty minutes on prompt technique, model comparison and how much faster the demo was than doing it by hand.
The demo is real. The function works. And it is, I would argue, the least consequential thing on the stage.
Here is why. Typing was never the expensive part of software. Ask any engineer with a decade behind them where their week goes and almost none of it is producing characters. It goes into understanding a system nobody documented, deciding what “done” means for a request that arrived as one sentence, waiting for a review, waiting for a deploy, finding out that the thing that shipped is not the thing that was wanted, and explaining status to people who need it in a different format than the one it exists in.
Code generation solves the one part that was already cheap. That is not a criticism of it. It is enormously useful, I use it constantly, and I would not go back. But if you want to understand why teams that have adopted it hard are not experiencing a proportional improvement in delivery, you have to stop looking at the generation and start looking at what generation was standing next to.
The load-bearing side effects of writing code by hand
The act of a human writing a change produced a great deal of value that had nothing to do with the change.
It produced understanding, in a person, that persisted. An engineer who implemented the billing reconciliation now knows how billing reconciliation works, and will know it in eight months when it breaks at two in the morning. That knowledge was a by-product. Nobody planned it, nobody paid for it directly, and it turned out to be one of the most valuable assets the organisation had. Generation does not produce it. You can read the output and understand it, but reading is a weaker encoder than building, and everyone who has tried both knows this.
It produced a natural rate limit that the rest of the process was calibrated to. Review, QA, release cadence, on-call load, even the sprint itself: all of it was tuned to how fast humans could produce changes. That rate limit was not a design decision, it was physics, and every downstream process quietly assumed it. Remove the constraint at one point in the chain and everything calibrated to it is now mis-calibrated. This is the plainest reason why “we got faster and nothing improved” happens. You did not speed up the system. You sped up one stage and moved the queue.
It produced a decision record, badly, but it produced one. Between the ticket and the merge, a human made hundreds of small decisions and left traces of some of them: in the commit sequence, in the pull request conversation, in the shape of the code itself. Terrible documentation, genuinely, but it was correlated with reality in a way that formal documentation never was. When generation compresses that whole interval into one step, the traces go with it.
And it produced a check on the specification. This one is subtle and it is the most important. When a human picked up a ticket that said “users should be able to export their data,” they would get about ten minutes in and then ask a question. Which format? Does it include deleted records? What about the fields we do not show in the UI? The specification was ambiguous and the human noticed, because building forced them to confront the ambiguity.
An agent does not ask. It resolves the ambiguity, silently, plausibly, and ships. The specification is no longer checked by the act of implementation, and nothing else in your process was ever checking it, because nothing else had to.
That last one is the whole ball game. The bug is not in the code. The bug is upstream of the code, and it just lost the only reviewer it ever had.
What actually got harder
If you sit with a team that has been running agent-heavy for a while and ask what hurts, the answers are consistent and none of them are about code quality.
Review is the first. Not review quality, review arithmetic. If the number of changes triples and reviewer headcount does not, each change gets a third of the attention, and there is no version of “be more careful” that fixes a division problem. Worse, the triage signals that used to let reviewers spend their attention unevenly (this one is from a junior on an unfamiliar system, this one is a rename) are weaker, because the changes all arrive with the same fluent surface.
Status is the second, and it is the one people complain about most while thinking it is a minor irritation. The board is wrong. It was always a bit wrong, now it is wrong enough to mislead, and the ceremonies designed to correct it run on a weekly cadence against a problem that regenerates hourly.
Coherence is the third and the least discussed. Ten changes, each individually correct, each solving its ticket exactly, can collectively take a codebase somewhere nobody chose. When a person wrote all ten they carried a model of the whole and steered. Distributed across sessions with no shared memory, nobody steers. You get a system that is locally sensible everywhere and globally incoherent, which is a specific and unpleasant kind of technical debt because no individual change can be pointed at as the mistake.
And the fourth is confidence. Ask engineers whether they would sign off on a claim that everything shipped last month was correctly reviewed. The hesitation is the finding.
So why does the generation get all the attention
Because it demos. Because it is legible in thirty seconds to someone who does not write software. Because there is a large industry with a strong commercial interest in the framing that generation is the product. And because the alternative framing is boring: your specifications are vague, your record is stale, your review process is arithmetic-bound. Nobody sells a keynote on that.
There is also an honest reason. Generation genuinely was the hard technical problem, in the sense that it was the one nobody knew how to solve. The rest of it (link the change to the ticket, check the outcome, keep a trail) is not technically hard at all. It is just unbuilt, because until recently nobody needed it badly enough. Hard problems get attention and unbuilt problems get ignored, right up until the moment the hard problem is solved and the unbuilt one becomes the constraint.
That moment is now, roughly. Which is why the interesting work in the next few years is not model work. It is the deeply unglamorous business of making the surrounding system honest: acceptance criteria a machine can evaluate, status derived from source control rather than from memory, and a signed trail of what was changed automatically so that when something goes wrong you can find out what happened rather than reconstructing it. That is the layer GroundTruth is built for, and I will note that the reason it is buildable at all is that none of it requires a research breakthrough. It requires someone to decide it matters.
Generation got the attention for an honest reason: it was the hard technical problem, the one nobody knew how to solve. The rest was always understood and merely unbuilt, because it was not worth building until generation was solved.
Which means the interesting work of the next few years is not model work. It is the unglamorous business of making the surrounding system honest.
A different way to see the shift
Try this frame. For fifty years, software development had one scarce resource: the ability to correctly turn intent into working code. Everything about how teams organised was a response to that scarcity. Hire for it, review it, plan around its throughput, defer to the people who had it.
That resource is no longer scarce. Not gone, not free, but no longer the binding constraint for most work.
When the binding constraint moves, an organisation built around the old one does not automatically improve. It usually gets worse for a while, because all its accumulated wisdom is about optimising a stage that no longer limits anything, and its instincts (hire more of the scarce skill, protect the scarce skill’s time) now point in unhelpful directions.
The new scarce resource is the ability to say precisely what you want and verify that you got it. That skill was always valuable and always undersupplied, but it was never the constraint, so nobody organised around it. Now it is, and almost no team is set up for it: not in how they write tickets, not in who they promote, not in what their tooling captures.
Where this breaks down
I have been fairly confident above, so here is the case against.
Generation quality is not fully settled, and my “solved” is doing too much work. On greenfield code in a popular language with a clean context, yes. On a fifteen-year-old codebase with three overlapping conventions and a build system nobody understands, generation is still frequently unhelpful, and for those teams the interesting problem genuinely is still generation. If you work in that world, most of this piece will read as premature.
“Typing was never the bottleneck” is an aggregate claim and aggregates hide people. For an experienced engineer on a familiar system, absolutely true. For someone learning a new framework, or working across an unfamiliar stack, or building the fortieth CRUD endpoint, the mechanical production of code really was a large share of the effort, and removing it is not a marginal improvement, it is transformative. I do not want to talk anyone out of a benefit they are actually feeling.
The side effects I described may be recoverable, and I have argued they are lost. Understanding-through-building can partly be replaced by better system documentation, which agents are quite good at producing. The natural rate limit can be reimposed deliberately. The decision record can be captured if you require agents to record what they considered. None of these is as good as the original, but “degraded and explicit” may beat “excellent and accidental,” and I have not proven otherwise.
And there is a real chance the specification problem gets solved by the same technology that caused it. A model that reads a ticket and asks the four clarifying questions a good engineer would ask is not far-fetched. If that lands well, the strongest argument in this piece weakens considerably. I would still want the verification layer, because “the machine asked good questions” is not the same as “we checked the outcome,” but I would be making a smaller claim.
The takeaway
Code generation is the visible half of this shift and the settled half. The unsettled half is everything the act of writing code was quietly doing on its way past: creating durable human understanding, rate-limiting the whole system, leaving a decision trail, and forcing someone to notice when the specification was vague.
Those side effects were free. Now they are not, and the ones that matter have to be built deliberately, which is a considerably less exciting project than the one everyone is watching.
The next piece follows the constraint to where it actually moved. If typing is no longer the bottleneck, something else is, and naming it precisely turns out to change what you should do on Monday.