A pull request lands in your queue.
Eight hundred lines. The description is excellent: clean summary, test plan, edge cases, a Mermaid diagram. Better than most descriptions you got from humans two years ago.
You start reading. Nothing is obviously terrible.
That would be easier.
The code compiles, the tests pass, the abstractions look reasonable. But after twenty minutes something starts bothering you. One retry looks suspicious. A transaction spans a boundary you didn't expect. A test mocks exactly the integration you wanted exercised for real. An error gets converted into another error that looks equivalent, except you remember an incident where that distinction mattered.
So you dig. You open another repository, read an old ADR, search Slack, check the contract of a service nobody has touched in six months. Then you go back to the pull request and ask the author: why does this transaction cross that service boundary?
A few minutes later:
Not sure. Claude added that.
The engineer who spent twenty minutes producing the change has handed you two hours of work. Not because they're lazy, and not because the code is wrong. Because someone still has to establish whether the code is right, and increasingly that person is not the person who produced it.
Most of the conversation about AI and programming is about generation: how much code it can write, how fast, how autonomously. But software organizations don't exist to generate code. They exist to operate systems. AI made generation cheaper. It did not make judgment cheaper, or accountability, or production incidents, or the consequences of a wrong assumption. That difference has to be paid somewhere, and right now it's being paid by the people who still check.
Cheap code sounds strange because software engineers are expensive, so you'd expect writing software to be expensive too. But source code has been getting cheaper for decades: machine code to assembly, assembly to C, C to garbage collection, hand-rolled networking to libraries, racked servers to cloud APIs, CRUD by hand to frameworks. Every generation of tooling let us express more behavior with less typing. AI continues the trend; the magnitude is what's new. A candidate implementation, hundreds of contextually plausible lines, now costs seconds.
Candidate is the operative word. A candidate implementation is not software. Software includes understanding the problem, choosing boundaries, discovering hidden invariants, reviewing, deploying safely, debugging failures, maintaining the thing three years later, and explaining why something strange exists. Code is one artifact in that process. For a long time writing it consumed enough effort that we used it as a rough proxy for the whole. AI broke the proxy, and organizations haven't adjusted. I've made the working-memory half of this argument in The bottleneck was never writing code: review was always the choke point, and AI floods it.
The most common frustration in the Stack Overflow 2025 Developer Survey, reported by 66% of respondents, was not spectacular hallucination. It was answers that are almost right.
Obviously wrong code is cheap. It announces itself, and rejecting it takes no expertise. Almost-right code compiles, passes the obvious tests, looks idiomatic, and arrives with a convincing explanation. The bug only appears when you know something that was missing from the prompt: an invariant, a historical constraint, a production behavior, a fact about how this particular company works.
So the asymmetry: producing plausibility became cheap while disproving plausibility stayed expensive. If a pull request used to take four hours to write and one to review, and an agent cuts the writing to thirty minutes while review grows because the diff grew, the org chart will report that implementation got eight times faster. The system didn't get eight times faster; the cost moved.
The psychologist Frederic Bartlett showed in 1932 why the almost-right slips through. He had English students read a North American Indigenous legend, then retell it over weeks, months, years. What came back was never the story: canoes turned into boats, the ghosts that gave the legend its name faded away, and every retelling came out shorter, more coherent, more English. Nobody noticed they were making things up. His conclusion became a foundation of psychology: memory is not a recording but a reconstruction, assembled from molds he called schemas. Bartlett never watched anyone read code, but I'd argue reading works the same way. You don't process code token by token like a compiler. You glance, recognize a mold, "this is standard JWT validation," and the schema fills in the rest. What the schema fills in is what the code should do. Not what it does.
function verifyToken(token) {
const decoded = jwt.decode(token, { complete: true });
if (!decoded) throw new AuthError('invalid token');
return decoded.payload;
}Idiomatic name, error handled, immaculate formatting, and the function verifies nothing: jwt.decode only deserializes the token without checking the signature, since validation is jwt.verify's job. Any syntactically valid forged token gets through. While reading, though, your "JWT validation" schema delivered the meaning before you reached the third line. The canoes turned into boats and you never saw the swap.
With code written by people, this mechanism had a statistical counterweight. Humans fail noisily: someone who doesn't understand the problem tends to write code that also looks confused, with crooked names and odd structure, because surface and substance come out of the same head. So "code that looks good is probably good" worked as a dirty but correlated proxy. An LLM breaks that correlation by construction. It generates, token by token, a statistically plausible continuation of everything it has seen, which means the most familiar possible version of any code: the canonical formatting, the dominant idiom, the name everyone would pick. Familiarity is, in effect, the function it optimizes, and the fluency comes out perfect even when the logic comes out wrong, because both are produced by the same blind process and only one shows on the surface. A human's bug trips over your schema. An LLM's bug wears your schema as a uniform.
There's measurement under this. In 2018 Sarah Fakhoury, advised by Venera Arnaoudova, put fifteen computer science students in front of buggy code snippets while an fNIRS sensor estimated cognitive load from blood oxygenation in the brain. Snippets with linguistic antipatterns, names that lie about what the code does, significantly increased measured load and held the gaze. Snippets with structural mess, formatting that breaks convention and unnecessary loops, slowed the students down but never moved the sensor significantly, however much they complained. The mess at least announces itself; the lying name extracts its cost in silence. And formatting, convention, and style consistency, the surface the model nails with superhuman perfection, is exactly the signal that predicted the least. Your bad-code detector was calibrated over a whole career on a population where ugly surface betrayed rotten substance. Bad human code tends to look bad. Bad AI code looks great regardless.
The confidence comes bundled. Neil Perry, Megha Srivastava, Deepak Kumar, and Dan Boneh published a study at ACM CCS 2023 in which 47 participants solved security-sensitive programming tasks, 33 with an AI assistant and 14 without. Those with the assistant produced insecure solutions more often in four of the five tasks, and believed more strongly that their own code was secure. The output came out fluent and canonical, the schema clicked, and schema fit is what the brain uses as the feeling of understanding. The feeling went up while the security went down, and nobody felt the lines cross.
Push the mechanism to its limit and it becomes an attack category. Joseph Spracklen and colleagues published at USENIX Security 2025 an analysis of 576,000 generated code samples: 19.7% of the suggested packages did not exist, and the hallucinations repeat, with 43% of the phantom packages reappearing in all ten runs of the same prompt. An attacker collects the recurring phantoms and registers the names on PyPI or npm with malware inside; the practice got a name, slopsquatting. The researcher Bar Lanyado watched models keep inventing a Python package called huggingface-cli, registered it with an empty, harmless package, and collected more than 15,000 real downloads in three months, including install instructions in the README of GraphTranslator, an Alibaba research project. A package name is the smallest piece of code there is, a string, and even that can't be validated by eye, because the criterion the eye applies, "looks like the right name," is the criterion the generator optimizes. Now scale from one string to the eight-hundred-line diff in your queue.
Economists call a cost created by one actor and paid by another an externality. The term isn't a perfect fit, since a reviewer sits inside the same company rather than outside the transaction, but the structure is identical: an unpriced spillover, running through the org chart instead of the atmosphere. It's the same mechanism I described for meetings in The attention economy inside your company: capture the benefit yourself, distribute the cost across everyone else. AI hands that trick to code. The author collects the benefit of cheap generation and moves on to the next ticket; the reviewer inherits the verification and keeps reconstructing reasoning that should have existed before submission. One gets the visible output. The other prevents the invisible failure.
I call this the AI review externality, and I think it's becoming the defining organizational problem of AI-assisted engineering.
A developer can ask an agent for a thousand lines before lunch. The reviewer cannot type /understand 1000 lines and acquire confidence. They still have to reason. What assumptions does this implementation make? Does this operation need to be idempotent? What happens when the process crashes here? Is this test validating behavior or reproducing the implementation? Was the old code accidentally weird, or historically necessary? None of that is autocomplete. It's deep comprehension work, and the context it requires is concentrated in experienced engineers. Which produces a paradox: the more generation accelerates, the more valuable experienced judgment becomes, and the more miserable we can make the people providing it.
There's early telemetry pointing the same way: Faros AI's 2025 analysis found teams with heavy AI use merging 98% more pull requests while review time rose 91%, observational numbers from a vendor, but the direction matches what reviewers keep reporting.
Vella and Blincoe followed developers across two questionnaires six months apart in a 2026 longitudinal study, still a preprint. In the second wave, 82% of respondents reported spending less time writing code. The work didn't vanish; it migrated: among the 88 participants matched across both waves, the balance shifted significantly from creation toward verification, clearly enough that the authors named the emerging category: supervisory engineering work. Direct the machine, inspect the output, correct it, verify it, repeat.
Maybe many engineers will love that job. Some already do: they run agents across unfamiliar repositories, prototype in days what used to take weeks, delegate boilerplate, explore three implementation strategies at once. For them the transition feels like liberation.
But there's a mistake hidden in the success story: assuming that because the new work is higher-level, it must be more satisfying. Management is higher-level than programming, and not every programmer wants to manage. Architecture is higher-level than implementation, and not every engineer wants to spend the day drawing boxes. Supervisory engineering is intellectually demanding. That doesn't mean people who fell in love with building software will enjoy supervising software-producing machines. Some will. Others won't. Companies are acting as though the preference doesn't exist.
When engineers say AI makes programming less enjoyable, the standard reply is that software engineering was never about typing. Of course it wasn't. Writing was never about pressing keys either. Nobody loved programming because semicolons were fulfilling. The craft lived in the loop between thought and material: you had an idea, tried to express it, the computer resisted, your model turned out incomplete, the awkwardness of the code exposed a deeper design problem, a nasty bug forced you to finally understand the scheduler. Eventually the program worked, and you were different too. The process that produced the software also produced the engineer.
AI doesn't only automate output. It can automate the feedback loop through which expertise develops. It doesn't have to. An engineer who asks "explain why this implementation fails under concurrent access" may learn something. The engineer who asks "fix it" gets a patch. Both close the ticket. Only one closes the knowledge gap. And organizations currently reward closing the ticket.
Software engineering always ran on apprenticeship, even when nobody called it that. Juniors wrote code, seniors reviewed it, and the questions did the teaching: why did you model it this way, what happens when this call fails, what does this query do at a hundred million rows. The pull request was a teaching surface. The junior arrived with a mental model, the senior challenged it, the model improved.
Now a junior can submit an implementation close to what a senior would write. Superficially that's excellent: the code-quality gap shrinks. But the artifact can outrun the author's understanding. Not a new condition, since copy-paste and outsourcing produced it long before AI, but AI produces it at a new scale with fewer visible signs. We know how to review a junior who writes junior code. Reviewing a junior who submits senior-looking code is harder, because now you have to infer two things: whether the code is correct, and whether the author understands it.
It also weakens something review used to rely on. Reviewing human code means reviewing the artifact and the reasoning behind it. Ask why they chose this, and the answer tells you something even when you disagree: there was a causal chain between intent and implementation. A generated pull request can contain decisions nobody consciously made. Systems always accumulated decisions nobody made, but they used to accumulate at the speed humans could produce them. An agent produces them wholesale: not bad decisions, just ones that emerged from whatever mixture of statistics, retrieved context, and test runs it worked through. Why is this retry exponential? Why is this interface split into three methods? Sometimes the honest answer is: the model produced it and it looked fine.
Code like that accumulates decisions without accumulating explanations, and that isn't technical debt. This is comprehension debt, Jason Gorman's name for the gap between how much code a system contains and how much of it any human understands: the code can be perfectly clean, beautiful even, and still nobody deeply understands why it has the shape it has. The system contains more behavior than the organization contains understanding.
Comprehension debt stays invisible until change arrives. Six months later someone has to modify the behavior, and now they must reverse-engineer not just what the system does but which parts of its structure are essential. With human-written systems some intent survives in people's memories, design docs, review threads, incident histories. Generated code can grow much faster than that shared memory: the codebase expands and the map doesn't. The first people to feel it are the seniors who maintain the cross-cutting parts: archaeologists of decisions nobody remembers making.
Then management arrives. AI promises productivity, and no organization responds with "wonderful, everyone works six-hour days now." It responds with "wonderful, raise the target." HackerRank's 2025 Developer Skills Report: 67% of developers said AI increased the pressure to deliver faster, and 84% of engineering leaders said they had raised productivity expectations. This might be the most predictable result of the entire transition. When one of my teams shipped a six-week tool in two weeks, product's answer wasn't a month off, it was tripling the scope.
The trouble is that targets rise before anyone measures the system-level cost, and the inputs to those targets are, in every org I've seen, self-reports. METR's 2025 randomized trial put a number on that gap: experienced open-source developers who believed AI made them about 20% faster were measured 19% slower. I've leaned on that study before, and it's one population with early-2025 tools, not a universal coefficient. But it shows the failure mode: subjective acceleration is a poor proxy for net work saved.
So an engineer gets faster at producing code, the expected output rises to match, review slows down, incidents tick up, seniors spend more time validating, and the organization consumes the entire dividend while increasing everyone's cognitive load. The engineer experiences AI not as a lever but as a treadmill that speeds up whenever they learn to run faster.
At this point the conversation usually retreats to outcomes. Did the team ship more? Did cycle time fall? Did customers get the feature sooner? Outcome sounds like the mature alternative to obsessing over process, but most of what gets presented as an outcome is output with better branding: pull requests merged, tickets closed, lines changed, features released, AI adoption reported. Those are events in a production system. They say that something moved. They do not say whether it was worth moving, whether it will keep working, or who absorbed the cost of moving it.
A merged pull request is not an outcome. It is a transfer of obligation into production. The method matters because the method determines what travels with the code: understanding, tested assumptions, recoverability, ownership, and the ability to change it later. Two teams can produce the same number of features this quarter. One keeps changes small, makes authors defend their assumptions, and leaves the people operating the system able to explain it. The other generates large patches, lets reviewers reconstruct the reasoning, and treats green tests as ownership. Their dashboards look identical until the environment changes. Then the method becomes the outcome.
The delay creates a politics of attribution. The person generating the change receives credit now. The reviewer prevents a failure nobody can see. The operator inherits the incident months later. The maintainer pays the comprehension cost after the original context has disappeared. Success stays local and legible; consequences become collective and delayed. Without an explicit policy for responsibility, the organization has built a machine for privatizing credit and socializing consequences.
So accountability cannot stop at "the team owns the outcome." Who must be able to explain the change before it merges? Who signs off on the assumptions? Whose time is counted when generated code needs two rounds of review? Who remains responsible when the implementation fails outside the test suite? If those questions have no answers, outcome language becomes moral cover for volume. The method disappears from the dashboard, the consequences disappear into operations, and responsibility appears only after there is someone to blame.
Take two engineers on the same team. The first runs three agents in parallel: one updates an endpoint, one writes tests, one prepares a migration. They review the output lightly, fix the obvious problems, open three pull requests. The second engineer reviews those changes. They notice the migration acquires a lock that will stall production writes, that the tests validate mocks instead of behavior, that the endpoint changes the semantics of a field another service consumes. They ask questions, read historical context, reject an abstraction.
At the end of the week the first engineer has three merged PRs and a lot of visible motion. The second produced nothing visible: an outage that didn't happen, an API break that never reached customers. Output like that was always hard to measure. AI makes the asymmetry worse, because generation becomes more visible at exactly the moment judgment becomes more important.
If the organization rewards what it can count, the rest of the story writes itself. The careful engineer reads everything and falls behind on tickets. Their manager asks why AI hasn't boosted their numbers like everyone else's. "I'm spending my time reviewing everyone else's generated code" sounds defensive. The PRs are too large, they say; the answer is to use AI to review them. The team's understanding of the codebase is deteriorating, they say; the answer is a dashboard where velocity is up 22%. Eventually the engineer learns the organizational lesson: being right is expensive, deep review is expensive, and none of those costs appear next to their name. So they adapt: they skim, they approve, they stop asking the third question. Their productivity improves, the AI rollout gets celebrated, and six months later nobody can explain the system.
That's how an engineering culture dies without the bar ever being lowered officially. You make exercising the bar economically irrational.
Open source is the extreme case because a contributor's generation cost can approach zero while the maintainer's verification cost stays real. Anyone can generate an issue, a feature request, a vulnerability report, or a pull request in minutes. A maintainer still has to evaluate it.
LLVM's AI contribution policy names the problem outright: generative tools can shift effort from the person creating a contribution onto the people reviewing it, and maintainers may reject contributions that aren't worth the review time they consume. OpenSSF has been dealing with a flood of AI-generated vulnerability reports polished enough to demand human validation and wrong in ways only real security expertise can establish. That's a denial-of-service attack on expertise, my framing rather than theirs, and it needs no malice: generation is cheap and adjudication isn't. curl estimated that around 20% of its 2025 bug-bounty submissions were AI slop and only about 5% were genuine vulnerabilities, and it ended the paid bounty program on February 1, 2026.
The lesson isn't "AI bad." The lesson is that when submission costs collapse, verification systems have to change. Enterprise teams are walking toward the same wall, one pull request at a time.
The obvious answer: if AI creates too much code for humans to review, have AI review it. We already do, and it helps with obvious bugs, missing tests, suspicious conditions, security smells. I expect AI review to become ubiquitous.
But it reduces verification cost without absorbing accountability. Somebody eventually decides: are we willing to operate this change in production? When a payment duplicates, the model doesn't join the incident call. When customer data leaks, the model doesn't face the regulator. Responsibility terminates in humans, and the more automation we stack before that point, the more important it becomes that the final human understands what they're accepting. Otherwise we haven't automated judgment, only obscured it.
The useful distinction is not human-written versus AI-written. That boundary is already gone: a human prompts, an agent writes, the human edits three lines, another agent adds tests, the reviewer asks a model to suggest a fix. Who wrote it? The question barely parses. The distinction that matters is owned versus unowned. I don't care whether you typed every line. I care whether you own every line you're asking someone else to trust: you understand the change, can explain why it exists, know the failure modes, validated the assumptions, and will be there to debug it when it breaks. "I don't know, Claude did it" should carry the same professional weight as "I copied it from Stack Overflow and didn't read it." Using Claude isn't the irresponsible part; submitting code you don't understand is.
The reviewer must never be the first person who seriously verifies generated work. Too many workflows today are prompt, generate, tests green, open PR, which quietly converts the reviewer into the first real evaluator. Put the author steps back in: read it, validate the assumptions, test the behavior, simplify, then open the PR. That's slower for the author, and it should be: some of the verification cost belongs with the person collecting the generation benefit.
When you do review an agent's diff, retire the signals that died. Formatting, convention, idiom, well-chosen names: in a human coworker's PR they're still informative, in an agent's diff they're what the machine emits by default, at zero cost, with none of the correlation to correctness they used to carry. Reading an AI diff admiring the cleanliness is judging a candidate by the tie the store clerk knotted on him. What survives is the fidelity between name and behavior, the one thing the model can't guarantee. Take every name that carries a promise (verify, sanitize, ensure, retry, is, has) and check the promise against the body, deliberately, the way you check a translation rather than read prose. It's slow and unnatural on purpose: the schema wants to accept verifyToken as verification, and the job is exactly not letting it. For dependencies the rule is shorter: a model-suggested package name isn't trusted, it's looked up in the registry and checked for downloads, maintainers, and history. Thirty seconds that huggingface-cli turned from paranoia into basic hygiene.
Keep pull requests small, more aggressively than before. Agents make large changes easy to produce, not easy to understand, and DORA's data shows the system-level bill: a 25% increase in AI adoption was associated with 1.5% lower delivery throughput and 7.2% lower delivery stability, which DORA attributes partly to AI enabling larger batches that are harder to review safely. If generation capacity grows tenfold, batch-size discipline should tighten, not relax. The bottleneck moved, and the process has to move with it.
Treat verification as production work, not overhead. Feng, Afroz, and Sarma surveyed 442 software professionals for ICSE-SEIS 2026 and found GenAI adoption associated with burnout through organizational pressure and workload, with participants describing colleagues' AI output as extra work that lands on them: cleanup, subtle bugs, review that keeps interrupting everything else. The study is cross-sectional, so association rather than causation. Still, run the accounting from the engineer's side: suppose AI makes them 30% faster while 30% of their week now goes to validating other people's AI output. The company's adoption success and the engineer's worse job are both true, and unrecognized labor curdles into resentment.
And stop measuring adoption. Shopify made "reflexive AI usage" a baseline expectation, with AI questions folded into performance review, where reflexive means considered by default, not used on every task. Coinbase went further: Brian Armstrong described requiring engineers to onboard onto Copilot or Cursor within a week and firing some who didn't without an explanation he accepted. I understand the executive logic, because if the technology matters this much, inertia is dangerous.
But "everyone must become competent with AI" and "everyone must visibly use AI" are different policies. Competence is a capability. Usage is a behavior, and once usage feeds performance review, Goodhart arrives in the IDE: engineers use AI where it's slower, generate code that creates more review cost than it saves, and the adoption dashboard glows. A mandatory productivity tool cannot fail honestly. Every failure becomes a prompting problem, a training problem, an attitude problem, anything except evidence that the tool didn't fit the task. DORA's guidance is the sane version: encourage experimentation, protect learning time, and leave the choice of when to use AI with the person closest to the work.
The measurement that would settle the productivity debate is total human effort per good change safely in production: implementation, prompting, reading and rewriting generated output, review, re-review, debugging, post-merge churn, incident response, and the cost of rebuilding context later. If AI reduces that total, use more AI. If it moves effort from the author to a reviewer, or from a mid-level engineer to a staff engineer, the story is weaker than the dashboard says. Right now most accounting stops when the pull request opens.
What pulled me into this topic was noticing experienced engineers describe a strange disengagement. Not "AI is going to take my job." Closer to "I don't know if I want this job anymore." Engineers describing their day as continuous review of generated output, people feeling judged on visible participation in the current corporate priority rather than on engineering outcomes. A 2025 request on GitHub's own community forum asked for a way to let repositories block Copilot-generated issues and pull requests, because handling them was itself becoming a maintainer burden.
These are anecdotes, not population statistics. Reddit is not a representative workforce survey, and people who love agentic coding don't write posts titled "everything is great and I remain professionally fulfilled." No dataset shows a mass exodus of craft-oriented engineers, and honestly, one may never come. But the mechanisms behind the stories now appear in the systematic research too: organizational pressure, more verification work, reduced flow, higher cognitive load, a shift from creation to supervision. Waiting for the attrition dataset before caring would be a strange management strategy. By the time your strongest engineers show up in the attrition metric, they've already left.
And they rarely go straight from engaged to resigned. They withdraw first: stop arguing, stop reading every line, stop mentoring deeply, stop caring whether the second implementation is better than the first. It's the fading I described in Why your best engineers are fading, driven by the mechanism from Engineer's cynicism is a symptom, not a personality: someone who cared deeply learns, through repeated experience, that caring hurts. What's left is doing exactly what's required and optimizing the metrics. In software that's lethal, because quality depends on discretionary effort. No process can require "notice the subtle thing that might matter later." That behavior comes from care, and you can create conditions where care is likely, but you can't mandate it. If your AI transformation taxes the people who care most, they can stay employed for years after they've stopped providing the thing that made them valuable, and the resignation letter arrives long after the caring stopped.
I don't worry much about AI producing bad code. Humans produce plenty, and the models, the tests, and the review tooling will keep improving. I worry about what organizations learn along the way. That generation equals productivity. That review is overhead. That comprehension is optional if tests pass. That questioning a generated change means resisting AI. That the people with the strongest quality instincts will gradually discover those instincts are career liabilities.
One afternoon another beautifully formatted 1,200-line pull request will arrive.
Excellent description. Full test suite. Green CI.
The reviewer will notice something strange around line 430.
For a moment they'll consider investigating: opening the old incident, reading the neighboring service, understanding why that code path is unusual.
Then they'll look at the other twelve pull requests waiting for review. The roadmap. The velocity dashboard.
And click Approve.
That's the moment to worry about. Not when AI writes most of the code, and not when an agent closes an entire ticket by itself. The moment the engineer who senses something is wrong stops investigating why. AI didn't make judgment unnecessary. We made it too expensive to exercise.




