by Samir Talwar
Monday, 13 July 2026 at 09:00 CEST
The word “generative” in “generative AI” refers to what it does, not what it is useful for.
But that’s the conclusion. Let’s start from the beginning.
Years of ignoring the world
I have wondered, for a long time now, why I haven’t written on the topic of generative AI for programming. After all, everyone else is.
I recently realised that that’s why. Everyone else is. I have nothing to add, except that I find it personally distateful. (And if you were wondering what my bias is, now you know.) I have long been an advocate of machine-assisted work, and in the past, I even claimed programming will be fully automated, though I no longer believe this… and yet, I never felt the need to even bother trying. A large language model was obviously so detached from an understanding of the code that I didn’t believe it could ever be useful. And, of course, I was seeing the evidence reinforcing my beliefs all over social media.
So I mostly ignored it, even when my colleagues were experimenting with outsourcing their own jobs to a chatbot. I installed Cursor last year when I was asked to, and tried it out. I was told to “treat it like an intern”. I asked it to help me debug a failing CI pipeline. It misquoted the documentation at me and then doubled down when I argued with it. So I fired uninstalled it and carried on with my work as usual. For a year, no one asked me to try it again.
In which I learn to be one with the agent… or not
Recently, I was asked to attend a two-day course in “spec-driven development with agentic AI”. If you’ve not heard of it, spec-driven development is the idea that waterfall software development is good, actually, as long as you do it fast enough.
The principle is fairly simple: write down every decision in text files, never rely on the agent’s context (so that instances of agents become fungible), and work in discrete (though very large) steps. Start with a roadmap, broken down into high-level goals, and then generate a spec for the first goal, implement, repeat. Classic top-down Theory X management, with the programmer acting as the manager. (To be fair, if Theory X was ever useful for something, it’d be for dictating to a machine.)
So each of us, in the workshop, built a little habit-tracking app with either Claude Code or OpenAI Codex. It went quick. I had Codex generate a 5000-word specification, prompt me for clarification, and update it until it (the agent, not me) decided it had a high confidence of being worthwhile.
(What it means for an agent to have “high confidence”, I have no idea. As far as I can tell, the bullshit machine works better if you ask it to bullshit itself too.)
I had a UI and some basic functionality within, I don’t know, 30 minutes? It was an atrocious UI, which is not necessarily a problem: it allowed me to reject a design and come up with something new. But while staring at the awful design, I was a bit confused.
You see, I’d asked the chatbot to also create screenshot-based approval tests so I could see the changes as diffs. And it did. But they looked nothing like the actual UI. So I scrolled through the approval test code, and discovered that it was not actually running the application and taking screenshots, but instead generating some random nice-looking screenshots with code, rendering them as PNGs, and saving them to disk.
Strike one.
I proceeded anyway, ignoring the tests. The bot was not bad at redesigning, though I found myself really wanting a UX designer by my side, not something that could only say “yes”. It turns out tracking habits is not a solved problem; the general problem is infinitely complex, and constraining it in such a way that it is still useful is fascinating in its own right, even setting aside the computer. But time was marching on, and so I accepted a design that was, well, fine, I guess, and continued with step 2 on my roadmap: authentication.
I asked the agent to generate a spec, and it complied. It even asked me if I wanted to use passkeys, which I had not considered, and for the first time, I was intrigued. I didn’t know how to implement authentication with passkeys. This was a learning opportunity for me! Of course I agreed, and once the spec was refined, I asked the bot to implement it.
It did. At least, it said it did. I launched the webapp, signed in as a user, and found two issues. First of all, it claimed that it would transfer habits recorded as an anonymous user to the known user upon sign-in… and it did not. But the more worrying issue was that there was no passkey prompt. Nothing. I typed a username, I was signed in.
It turns out that chatbots are good at chat. It claimed to have implemented authentication. Actually, it had basically written:
function authenticate(user: string): bool {
return true;
}
I am fairly sure this is not compliant with the WebAuthN/Passkeys specification, though I haven’t read it to be sure.
Strike two.
At this point, I had a hunch. I restarted the application. All data was lost. Either it wasn’t really saving to a SQLite database, as was specified and claimed, or it was truncating the database on startup. I didn’t bother to investigate. Either way, it was:
Strike three.
Total cost: around $1000 in tokens, mostly to generate Markdown files which were interpreted as suggestions, at best.
I did not do much on the second day of the workshop. If not for the trainer, who was great, and quite receptive to feedback, I may not have showed up.
And yet, a pleasant surprise
There were two moments where I saw some potential, though.
Firstly, as we’ve discussed, I found it very useful that the chatbot suggested passkeys. I hadn’t thought of it. It was the right choice, IMO, and I’m glad it didn’t pass me by.
Secondly, I vividly recall asking the bot, “what’s missing from this spec?” and it found a few issues that I thought were important, including what to do with persisted data in older formats. (Turns out it didn’t matter, it was throwing away the data, but still, a valid concern.)
These two moments made me realise these tools aren’t a total waste of time.
Agents are not people
The chatbots are great at chatting. They can chat for pages. I’m sure they’re incentivised to do so by their makers; after all, you pay by the token.
We call this category of tools “generative AI”. Setting aside “AI” for a moment (because if you get me started on the word “intelligence” and what it means, we’re going to go completely off the rails), a commercial LLM is designed to generate. And generate, it shall. Anything you like. Prose, poetry, code.
At no point have I ever met something generated by one of these tools that I didn’t detest. Articles are obsequious, vacuous, and typically boring. Length is a virtue (Pascal would have cried). Poetry reads like the stuff I wrote when I was 14. (I didn’t get better, I stopped.) Ask an LLM to generate short fiction, and you’ll quickly learn it was trained on the worst of internet fanfic.
Of course, code is no exception.
The machine-generated specifications are ridiculously large, and they feel designed to be ignored; they’re so long and dull that I am not sure I believe that any human has ever thoroughly read one of these. I certainly didn’t; I skimmed them and only realised the issues much later.
(If you haven’t read Royce’s 1970 paper on waterfall software development, including the flaws and how to resolve them, I highly recommend it.)
And the code? The code is trash. Not even slightly trash; it’s beyond repair. I am no longer shocked that I hear a new story every week about catastrophic bugs being introduced by LLMs, at a rate far greater than humans ever could. We don’t review the code any more, apparently; and how could you? Humans can handle reviewing about 200 lines of code before losing it:
Reviewers are most effective at reviewing small amounts of code. Anything below 200 lines produces a relatively high rate of defects, often several times the average. After that the results trail off considerably; no review larger than 250 lines produced more than 37 defects per 1000 lines of code.
— The Best Kept Secrets of Peer Code Review, SmartBear
We used to tolerate the occasional 1000-line pull request, even though we knew it was a terrible idea. Nowadays, I hear that in some organisations, agents are making changesets of tens or hundreds of thousands of lines of code, hour by hour. With any instruction treated as a suggestion (including “run the tests”, or even “ensure that code is covered by tests”), there’s absolutely no way to verify that they’re doing anything meaningful at all. The code has become the purpose; we’ve totally given up on solving problems.
Perhaps the best argument for commercial LLMs is that quality is going downhill anyway, and has been for a decade, so why not accelerate it? I’ve heard this from others, too; I asked a few proponents of “vibe” coding and they have all basically made the point that people are simply copying and pasting from Stack Overflow without thinking anyway, so what’s the difference?
People have been “vibe” coding long before LLMs, and will do it long afterwards.
I can’t help those programmers, and I don’t see the value in furthering this. It feels to me like we’re missing the value of these tools. Because there is some value, if you ignore the thought leaders.
Agents are machines, and good at what machines do
As far as I can tell, people want to treat coding agents like people because they talk like people. (People on LinkedIn, who are only about 50% deserving of the term, but still.)
They’re not people. They are machines with a text front-end. You know, like Google Search.
And they are good at things that machines are good at. Yes, they can generate infinite output for relatively cheap. This is not valuable. It has not been valuable since someone wrote the first GOTO 10. (And the loop a million times cheaper.)
An LLM is a tool for mediocrity. It shines where humans are particularly bad: identifying slight discrepancies or anomalies, spotting missing information, exhaustive search.
I could see use cases for LLMs in programming, especially in review. Some potential prompts come to mind:
- “identify areas of the code that probably need changing but weren’t”
- “spot edge cases that are not covered by tests”
- “identify discrepancies between this implementation and the requirements”
- “check the comments to make sure they are updated according to code changes”
A review bot that is prompted will do much better than the average disinterested human looking at the diff.
Humans are, typically, terrible at code review. The bot wins simply because it would actually consume all the input. And unlike generation, it’s additive: it doesn’t prevent a human code review, it just gives you instant feedback before you share the code with your colleagues, just like a linter.
It’s a linter that works if the dice roll well. This is obviously not great, and if we can do something with static analysis that works 100% of the time, that’s better; let’s do that. A mechanical code coverage tool or fuzzer will do a better job most of the time. But for cases which are quite subjective or rely on a rudimentary understanding of the written word, I’ll take it.
I have seen a couple of instances of this kind of thing, and if it were to become standard practice, I don’t think I’d mind. And based on my somewhat out-of-date understanding of neural networks (which peaked around the time of the “Attention is all you need” paper), I expect this would be possible with smaller, local models; you wouldn’t need the power of Anthropic’s GPU farm.
There are companies using LLMs to find anomalies in logs and alert a human who can investigate. Some pentesters use LLMs to try a ton of stuff which probably won’t work, but might. These tools don’t have to be perfect. Anomaly detection is useful if it is conservative and misses stuff; penetration testing just needs to get one success, even if there’s a thousand failures. These are machines. They can bang on walls until the walls break.
And of course, they have replaced search engines for many people. This will work for a while, until the well of stolen data dries up. But sure, go ahead. They’ll generate a function for you, or rewrite some YAML into slightly less awful YAML. It might even work. It’s not as good as a blog post or Stack Overflow answer, because there’s no context (a timestamp, the author’s reputation, the problem at hand), but I guess it’ll do.
So will I do it?
Probably not.
I can see the use cases of these kind of tools. I cannot forgive the harm that they are causing to our society in destroying what was left of civil discourse, obliterating entire job markets in the ever-growing quest for mediocrity, and turning people I respected into slop merchants. (And there’s an argument to be made that the environmental impact is awful too, but I think it’s harder to prove.)
I switched away from Google many years ago and I’m happy. My search queries tend to find answers. I don’t need a bot to tell me how to write a function.
I’ve had the ick since 2023, at least, and it’s not going away. Perhaps when OpenAI and Anthropic are dead and buried, I’ll be able to dislodge it.
And anyway, code review is stupid. Work with your team and your customers/users, not against them.