Vibe coding has two honest endings, and we industrialised the third
AI · Sep 2026 · 17 min read
Collins made it word of the year. The man who coined it now calls it a shower of thoughts he fired off without thinking. Read the original tweet to the end and it is not a technique for writing code at all — it is a technique for throwing code away, and we kept the half that generates.
Collins made it the word of the year in November 2025. Three months later, the man who coined it described the tweet as a shower of thoughts he fired off without thinking, said he had moved on to calling the thing “agentic engineering”, and noted in passing that his own most interesting project of the previous year was basically entirely hand-written.
Somewhere between those two events the term stopped describing anything in particular, and the argument about it hardened into two camps. One says this is how software gets built now. The other says this is how you get a security incident with your name on it. Both camps are arguing about a technique for producing code.
It was never a technique for producing code. Read the original tweet all the way to the end — which, going by what gets quoted, almost nobody does — and it is a technique for getting rid of code. We kept the half that generates and dropped the half that disposes, and three years of commit data is now showing us the bill.
The clause everybody deleted
The famous part is two sentences long and you have read it several hundred times: a new kind of coding where you fully give in to the vibes, embrace exponentials, and forget that the code even exists. What follows it in the same tweet is the part that makes it coherent.
The bottom half is not a disclaimer bolted onto the top half. It is the condition that makes the top half a reasonable thing to do.
He accepts every diff without reading it. The code grows past what he understands. When a bug will not die he asks for random changes until it goes away. And then the scope: it is not too bad for throwaway weekend projects.
Every one of those behaviours is defensible, and they are defensible for exactly one reason. Not reading the diff is fine when nothing downstream depends on the diff. Asking for random changes until the error stops is a perfectly rational debugging strategy for something you are going to close the tab on. The method is not reckless. It is precisely calibrated to a blast radius of zero.
It was never a claim about how to write software. It was a claim about how little you owe code that you are about to throw away.
Which is why the two camps talk past each other so reliably. The enthusiasts took a technique that was defined by its disposal step and kept everything except the disposal step. The critics attacked the technique rather than the amputation. Neither has much to say to somebody who has an agent running right now and a repository that will still exist in November.
Code has two honest endings
Here is the frame I have ended up using, and it is not about models at all. Every file that comes out of an agent ends one of three ways. Two of them are decisions somebody made.
The third box is not a decision anybody makes. It is what you get for free when the first two are not made.
Deleted is the ending the original tweet assumed. The prototype answered its question, the answer is worth keeping, and the code that produced the answer is not. Adopted is the other honest ending, and it needs a sharper definition than it usually gets, because it is not the same thing as merged, reviewed, or green.
The adoption test, in one question: If this breaks at 4pm on a Thursday in November, is there a person who can fix it without reading it the way you would read a stranger's code? Not a person who will be assigned it. A person who already knows what it does. If the answer is no, the code is in production and unadopted, whatever the pull request said.
Nothing in that question is about who or what typed the characters. A file a colleague wrote and nobody else ever read fails it too. Generation did not invent this failure — it removed the last thing that used to prevent it, which was that producing a thousand lines took long enough that somebody ended up understanding them on the way past.
The third ending is not legacy code
It is worth being precise about the failure, because it gets filed under a label that does not fit and then gets the wrong treatment.
Legacy code is code that somebody understood once. There was an intent, there were constraints, there was probably an argument about it, and all of that is recoverable — from the commit messages, the tests, the ticket, or the one engineer who was there and still flinches when you mention it. Archaeology works on legacy code because something is actually buried.
Legacy code is something a person understood and then forgot. Unadopted code is something nobody ever knew.
There is no excavation available on the second one, and every convention your team has for handling code it does not understand routes to a dead end. Ask the author — git blame says you. Check the pull request discussion — it says LGTM. Read the tests — they were generated from the same misunderstanding as the code, which is why a test suite needs something adversarial grading it . Find the ADR — there is no ADR, because nobody made a decision.
The practical consequence shows up as an unusually flat kind of incident. Not a hard bug — a bug that would be twenty minutes if anybody had the model in their head, taking four hours, because the first three are spent reading your own codebase the way you would read a stranger's .
You can see it in the commits
That would be an anecdote if the shape of it were not showing up in aggregate. GitClear and GitKraken read 623 million code changes from 2023 into 2026, a period over which roughly a quarter of commits came to show signs of AI assistance, and tracked maintainability signals across them.
Seven signals, two directions, one story: more code arriving, and less of anything that implies somebody went back.
Take the bottom bar on its own. Long-term updates — changes that touch code last modified more than twelve months ago — fell from 1.7% of changes in 2023 to 0.46% so far in 2026. That metric is not measuring code. It is measuring human behaviour towards code, and it says the rate at which anybody goes back to something that has been sitting there a year has dropped by roughly three quarters.
And the error-masking bar, up 47%, deserves to be read next to the tweet. Asking for random changes until the bug goes away is a sentence somebody wrote as a joke about a Sunday afternoon. This is the shape it takes when it lands in a repository that has customers.
# The bug did go away. So did the invoice.
try:
total = reconcile(batch)
except Exception:
total = 0
# Adopted: the failure is still a failure. It just no
# longer takes the batch down with it, and somebody
# finds out that it happened.
try:
total = reconcile(batch)
except ReconcileMismatch as e:
log.error("batch %s did not reconcile", batch.id)
raise RetryLater(batch) from e
What this data does and does not establish: It is correlation across an industry, not a controlled result, and some of the movement has other explanations — more greenfield work, more small repositories, changing composition of the sample. I would not lean hard on any single bar. What is difficult to explain away is seven independent signals moving the same way for three years, and in particular the last one, because it measures whether humans return to old code rather than what the code looks like.
Why the decision never gets made
If the two honest endings are cheap to describe, the obvious question is why the third one is winning. The answer is not laziness, and there is a measurement for it.
METR ran a randomised trial in 2025 with sixteen experienced open-source developers, 246 real tasks, in repositories they personally maintained and which averaged over a million lines. Before starting, the developers expected AI tooling to make them 24% faster. They came out 19% slower. Afterwards, having done the tasks, they still estimated that they had been 20% faster.
The slowdown is the headline and it is the least interesting part — one study, early-2025 tooling, a specific and unusually demanding setting, and plenty of legitimate argument about how far it generalises. The part that generalises is the roughly forty-point gap between what happened and what it felt like, and the fact that it survived the experience of doing the work.
The decision to adopt or delete falls due at the exact moment you feel faster than you have ever felt. Nobody audits a winning streak.
Then the second mechanism, which is about what the output looks like. The 2025 Stack Overflow survey put adoption at 84% of developers using or planning to use AI tools, and asked what frustrates them most. The top answer, at 66%, was solutions that are almost right, but not quite. Forty-five per cent said debugging AI-generated code takes longer than debugging their own.
Almost-right is precisely the profile that defeats a casual adoption check. Obviously wrong code gets deleted by the person who generated it, within seconds, for free. It is the code that reads plausibly, matches the surrounding style, and has a test next to it that also passes, which sails through and takes up residence. The failure mode is not that bad code gets kept. It is that almost-right code gets kept, and almost-right is the modal output.
The ending is chosen before you start
The useful move is to stop treating this as a judgement you make about the output and start treating it as a property of what you pointed the agent at. You almost always know the answer before the first token.
What you aimed it at decides how it has to end What you pointed the agent at The ending available to it What that ending costs A question — does this API actually behave the way the docs claim? Delete it. Write down the answer in two lines. Nothing. You were never keeping it. A spike you are demoing on Thursday Delete it, and say on the day that you are deleting it. One sentence of honesty during the demo. An internal tool that three people will use Adopt it lightly: one named owner, one test you wrote yourself. About an hour, once. Anything touching money, auth, or personal data Adopt it fully, or do not generate it. The read. There is no version of this that skips the read. A migration you will run exactly once Delete the migration after it runs — but adopt the rollback. The rollback is the part that executes while you are panicking.
That last row is the one I would defend hardest. The forward migration gets attention because it is the thing you are nervous about on the day. The rollback is the code that runs when the forward path has already gone wrong, in the fifteen minutes when nobody is capable of reading anything carefully, and it is almost always the part that got generated and skimmed.
Name the ending in the branch: The cheapest mechanism I have found is a prefix. spike/ branches cannot be merged, only summarised and closed — the CI rule is four lines and the social rule is the actual point. Everything else is adopt-or-abandon. Almost all of the value is that the decision now happens at branch creation, when it costs nothing, rather than at review, when it costs an argument.
Adoption is not code review
The reason the third ending survives contact with a functioning review process is that review is asking a different question and asking it well. Review asks whether the change is correct. Adoption asks whether the team can still move next quarter. A change can pass the first cleanly and fail the second completely.
The same pull request, two sets of questions Review asks Adoption asks Is this change correct? Could I have written this, and would I have written it this way? Does it follow the style guide? Does it solve this the way the rest of the system already solves it? Are there tests? Do the tests fail when I break the behaviour on purpose? Any bugs you can spot? What is the failure mode, and who gets paged for it? Approve, or request changes. Whose name is on this in November?
The right-hand column takes longer than the left-hand column, which is the whole problem, and it is the same problem as the one that moved to review and never got staffed . Generation got roughly an order of magnitude cheaper and reading did not get cheaper at all. Any process that assumes those two move together is going to keep producing the third ending no matter how diligent the reviewers are.
What does help is that reading is a job a model is genuinely good at — as long as you are careful not to ask it for a verdict on its own work.
# In a fresh conversation, with only the diff pasted in.
# Not the one that wrote it — it will defend its choices
# and it will be persuasive about it.
Explain what this code does, to somebody who has to
own it on call. List every failure mode it does not
handle. Name the three places a future change is
most likely to break it, and say why.
Do not tell me whether it is good.
The last line is doing most of the work, for the same reason that a grader should never be the thing that set the exam . Ask for an assessment and you get reassurance shaped like an assessment. Ask for an explanation and a failure list and you get the raw material for the read you have to do anyway, in about a fifth of the time.
The window closes faster than you think
One more thing about adoption that I got wrong for most of a year: I assumed it was a task I could defer, like documentation. It is not, because the expensive part of it expires.
The code does not degrade. The only thing that degrades is your ability to reconstruct why it looks like that.
While the session is open you have things that exist nowhere else: what you actually asked for, the two attempts you rejected, and the reason you rejected them. None of that is in the diff. A week later you are reading the same file with no more context than a new hire has, except that you are less careful, because it has your name on it and you vaguely remember approving it.
Which is the sharpest version of context being the job now . The context that makes adoption cheap is generated as a by-product of the session and thrown away when the tab closes. Ten minutes of writing at the end of a session is worth more than an afternoon of archaeology a month later, and it is the single highest-leverage habit I have picked up from any of this.
# spikes/2026-09-14-webhook-ordering.md
Q: does the provider guarantee per-customer ordering?
A: No. 3 out-of-order deliveries in ~400 over 20 min.
Sequence numbers exist but are not contiguous, so
gap detection does not work either.
So: the dedupe table is not optional. ~1 day.
# The 340 lines that established this are deleted.
# They were never the finding. This file is.
Where none of this applies
The failure mode of an argument like this one is that somebody reads it, agrees, and starts applying an adoption ritual to everything — at which point prototyping costs what it used to cost and the entire benefit has been handed back. So it is worth saying clearly where the two-endings rule is overhead you should refuse to pay.
Anything genuinely disposable: a one-off data pull, a script to answer one question, four throwaway versions of a layout to see which one you hate least. Vibe coding in the original sense is excellent at these and they should carry no ceremony at all. Anything a cheap, external, indifferent verifier already judges end to end — a typed build, a real test suite you wrote, a migration with a rehearsed rollback. The verifier has done the reading for you on the axes it covers. Personal tools with a blast radius that stops at you. If it breaks, you are the only one who finds out, and you will find out immediately.
There is also the fair reading of where the coiner has ended up. Karpathy's own position in February 2026 was that programming via agents had become a default professional workflow, but with more oversight and scrutiny, and that the term he prefers now is agentic engineering — not writing the code directly, but orchestrating and overseeing.
I agree with all of that and would change one word. Oversight sounds continuous and ambient, something you exercise while the work happens. What actually prevents the third ending is discrete: a specific moment, with a name, where somebody says this one is mine now, or this one goes. Ambient oversight is exactly what produces a repository full of code that everybody watched and nobody owns.
The nanochat detail is the rule working, not a gotcha: When he said his own project was basically entirely hand-written because the agents did not work well enough on it, that was not an admission that any of this is fake. It is novel code with no existing pattern to imitate — the case where generation is weakest and adoption cost is at its highest, so the two endings collapse into one. Most of us are not writing that, most weeks. Some of us are, on the one module that matters most.
What I actually do now
Four things, none of them clever. I name the ending before the branch exists, because at that point it is free and honest and later it is neither. I let the agent run without reading the diffs on anything in the delete column, which is genuinely most of it, and I enjoy it. I do the adoption read inside the same session for anything in the keep column, using a fresh conversation as a reader and never as a judge. And I write the five-line note when I delete something, because the note was always the deliverable.
What that adds up to is less exciting than either camp's version. It is not the end of programming and it is not a security incident waiting to happen. It is a cost that used to be bundled into the act of typing and is now unbundled, sitting there unpriced, and the only two ways to settle it are to read the code or to get rid of it.
The word will keep drifting; Collins can have it, and the coiner has already moved on. What survives the drift is the boring claim that was in the tweet all along, in the sentence nobody quoted: this is fine for things you are going to throw away. We took the method and kept the output, for about a year and a half, at industrial scale. The commit data is the invoice.
References Andrej Karpathy, the original tweet (2 February 2025) — Including the two sentences that rarely travel with it: the code grows beyond comprehension, and it is not too bad for throwaway weekend projects.Karpathy's one-year retrospective (February 2026) — A shower of thoughts throwaway tweet. Prefers “agentic engineering”; notes the professional default now carries more oversight and scrutiny.Collins Word of the Year 2025 — Programming by vibes, not variables. The moment the term stopped belonging to engineers.GitClear & GitKraken, The Maintainability Gap (2026) — 623 million changes, 2023–2026. Duplication +81%, error-masking +47%, refactoring moves −70%, updates to year-old code −74%.METR, Measuring the Impact of Early-2025 AI on Experienced Open-Source Developer Productivity — Expected 24% faster, measured 19% slower, still felt 20% faster afterwards. The gap is the finding.Stack Overflow Developer Survey 2025 — AI — 84% using or planning to use AI tools; 66% cite “almost right, but not quite” as the top frustration; 45% say debugging AI code takes longer.
Takeaways
The original vibe coding tweet ends by scoping itself to throwaway weekend projects. Not reading the diffs is coherent only when the blast radius is zero — the disposal step is what licenses the method. Generated code has two honest endings: deleted, with the finding written down, or adopted, meaning a named person can change it next quarter without re-deriving it. Merged and green is neither. The third ending — retained but never adopted — is not legacy code. Legacy code was understood once and can be excavated. This was never in anyone's head, so every route to understanding it dead-ends. Across 623 million changes from 2023 to 2026, updates to code older than twelve months fell 74% and refactoring moves fell 70%, while duplication rose 81% and error-masking constructs rose 47%. METR found developers 19% slower with AI while believing they were 20% faster. The adoption decision comes due exactly when you feel fastest, which is why it gets skipped. Obviously-wrong code gets deleted for free. Almost-right code — the top frustration for 66% of developers — is what survives a casual check and takes up residence. Choose the ending when you create the branch, not at review. Blast radius decides it, and for a one-shot migration the forward path is disposable while the rollback must be adopted. Adoption is a different question from review: not is this correct, but whose name is on this in November. Use a fresh conversation as a reader that explains and lists failure modes — never as a judge of its own work. The adoption window closes in hours, not weeks. What expires is not the code but the prompt, the rejected attempts and the reasons — none of which are in the diff.
All notes · Shehzad Aslam