Codificación con vibra: el antídoto

Vibe coding: the antidote

LLMs have shown themselves to be a terrible way to write programs. But the problem they address is real, and we aren’t solving it any other way. This is what we should be doing.

Jules May

Mickey Mouse, as the Sorcerer’s Apprentice, tries vibe coding. Image AI-generated by the author.

You already know the argument by now. On the one hand, non-programmers exult: “I can create what I want, from scratch, in a couple of hours, and it can be online and making me money this evening”. Meanwhile, older and (one would hope) wiser software professionals sadly shake their heads and say: “You’ll regret it.” And they do; they always do! Next day the vibe-coders complain: “Where did all those cloud costs come from?” or “Why is it selling all my products for nothing?” or “Somebody has hacked my store and now I’m selling drugs in Colombia.”

Vibe coding gives the impression of progress: LLMs can make toy-scale applications really quickly. But as you ramp up, the programs become harder to handle and less predictable. Although the process is supposed to be dialogical, in fact the AI doesn’t really understand what it has already got right, and so every would-be incremental iteration rewrites the whole program anew. The experience is not of building stepwise on past successes. It’s more like firing arrows at a target: two or three arrows might land reasonably close to the centre, but then the next ten might miss the target entirely. As the program grows, the experience becomes more and more chaotic, like the Sorcerer’s Apprentice who’s lost control of his cleaning tools.

“But look!” claim the vibe-coders. “I’ve done all this, and I don’t know the first thing about programming. How cool is that?” Rather than invest a little effort in learning how to program — or even in learning how to talk to a programmer — they celebrate their own amateurism and regard their apparent progress as its own virtue, despite the manifest risks.

I wish I could ask these vibe-coders: would you entrust your wealth and health to an amateur dentist? Or an amateur lawyer? Or an amateur electrician? Actually, yes, they probably would — people take such stupid risks all the time: social media and the obituary pages are full of them.

I’m mocking the attitude, but actually I sympathise with it. I live in a 300 year old house, which needs the constant ministrations of tradesmen. And yet frequently I have the experience, when consulting professionals, that they don’t understand my task. They complicate my problem beyond my ability to understand their diagnosis, they add time and costs out of all proportion to the task itself, and they brandish arcane regulation and best practice as if it’s a stick to beat me with. They stand merciless in the face of my puny “All I want is…”: they’re certain I need something else and will brook no disrespect.

I don’t know very much about building, just as the vibe coders don’t know very much about programming. But what we both do know is: our domain. We know what we want to achieve, even if we don’t know the hard mchanics of how to achieve it. And for both of us: what we want to achieve doesn’t seem very much. So, I can see why vibe-coding looks like a very attractive option. It promises to make something which seems easy, easy.

Real programmers, of course, have a different perspective. To us, coding isn’t the hard part. Actually, learning to code is just table stakes: the entry to our profession. What wraps around that — the regulation, the best practice, the systems-level design, the endless wriggling dependencies, the ever-expanding and officious toolchains, the soul-destroying sysops— those are the hard parts.

Writing code isn’t what we do. Writing code is how we deliver what we do. LLMs solve the simplest part of the programming task, badly. Why would anyone give any credence to them at all?

I’m a professional developer. I understand the difference between coding and programming, and I agree entirely with my fellow professionals’ opinion. But I don’t like it. I think it makes the vibe-coders’ argument for them.

Back in June 2014, Andrew Binstock, editor of the soon-to-be-defunct Dr Dobbs Journal, wrote an interesting piece called “Just let me code”. (No longer online, but referenced here.) It’s worth reading in full if you can find it, but I’d like to quote these particularly plaintive sections:

Managing the complexity of development toolchains — from SCM, to the build tools, to the testing, to the deployment stack — now so overwhelms the developer experience, it’s hard to get any real programming done…

The simple programs of a few hundred lines of C++ long ago disappeared from my experience. What was the experience of riding a bicycle has become the equivalent of traveling by jumbo jet; replete with the delays, inspections, limitations on personal choices, and sudden, unexplained cancellations — all at a significantly higher cost…

The fundamental problem here is not the complexity of apps, but the complexity of tools. Tools have gone rather haywire during the last decade chasing shibboleths of scalability, comprehensiveness, performance. Everything except simplicity.”

That was written more than a decade ago, but we’ve only gone downhill since then. Why, when our modern agile mindset counsels that delivered value is everything, do we constantly put impediments in the way of delivering that value?

In the days before graphical interfaces, all programming was console-mode (even if consoles — via things like curses— were a little more sophisticated than today’s). The languages we had then — C++, Pascal, Java in its early incarnations — matched that task perfectly. You could write something useful — either for desktop or industrial use — in (yes, as Andrew says) a few hundred lines. The Unix model of pipes and (tiny) filters reigned supreme. There was no real need for code repositories, for regression testing, even for peer review, because each development was small, self-contained, and most of all isolated (even when it participated in a larger system). You could grok it at a glance.

Arguably, the most complex programs we wrote back then were games. They had a uniquely real-time nature to them, they did a lot of computation, and they made significant demands on the hardware. In return, they were given sole tenancy of the computer: they had to cooperate with nobody and nothing.

And then, Windows 3 happened. All the rules changed. Now, the program wasn’t idling on its input stream. Now it was responding to events from who-knows-where? Our programs were still in C++ and Java, but now they were turned inside-out: the core of the program was an event loop (which, as anyone who remembers them will confirm, were bloated, unmaintainable abominations) and the actual behaviour resided in multiple procedures which nevertheless had to cooperate with each other and with the underlying system. The whole shuddering edifice needed pages and pages of initialisation to register new window “classes”, for no better reason than to provide somewhere to tell the operating system where your code was. It was an unmaintainable, sprawling, anti-modularisable mess.

That, I think, was when the first frameworks began to emerge. Microsoft Foundation Classes was the first: it wrapped up the system initialisation and message loop into a C++ class system, and it also objectified the Windows API (which, at its heart, was all plain, untyped C). It wasn’t very pretty, and it wasn’t easy, and it was full of gotchas, but it was loads better than the unwrapped version.

But Microsoft did something else. They created Visual Basic. I think this was one of the first approaches that really took the Windows bull by the horns. You created screens and dialogs using a visual editor — just dragging components into position — and then you wired them together using the simplest possible language: no objects or classes, no complex interconnectivity. Just simple “If this, do that”.

Visual Basic was a revelation, and by the time it had matured to version 6 it was, in my opinion (both at the time, and now), one of the finest pieces of programming technology the world has ever seen.

It was easy — easier even than Pascal. You could whittle a program incrementally, and the platform itself guided you in its use. I worked with loads of amateur programmers — practitioners who knew a lot about their domain but not much about software — who were able to mash up a simple program and then build a business out of it. I suspect even now that VB was responsible for more successful software startups than every other technology combined.

Of course, professionals sniffed at it. They hated the ugly glue-language, they hated the fact that it didn’t play nicely with Git, they hated that when they tried to use it ‘professionally’ their programs descended into a big ball of mud, but most of all they hated that it admitted the great unwashed into their trade. I think professional snobbery was partly why the market for third-party components never really developed.

Alas, VB didn’t last. It wasn’t at all suited to the newly-developing industrial software practices, such as Git and team monocultures. It didn’t help when Microsoft moved the underlying language into the new, schizophrenic, Napoleon-complexified .Net runtime, which made programming it as hard as programming C#, and destroyed the very thing that made it successful. (Now, even the visual editor is gone, and you have to create dialogs in xaml! The last traces of what made VB6 any good have finally been effaced.)

I’ve thought about this a lot, and the conclusion I’ve come to is: our most fundamental modern tools are simply unfit for purpose. The languages we use are, pretty much, predicated on 1970s and 1980s paradigms: single-threaded, procedural, deterministic, and using a very odd kind of modularity which we pretend is object-orientation (but in reality is nothing of the sort). Starting in the 1990s, and accelerating over the years, the programming task changed, even though the languages didn’t. Now, those languages are fundamentally unsuited to modern software needs: reactive, asynchronous, non-deterministic, built on unreliable instances and connectivity. All this heavyweight tooling, all this superposed legislation, all those dependencies: they are there to overcome that basic impedance mismatch and shoehorn our 21st century programming tasks into 1980s programming technology.

Coding is easy: the languages are still just as clear and expressive as they were three or four decades ago. Programming is hard because the languages we use (at least those middle-aged languages which most of us use every day) don’t speak to our current problems. Put simply: code is no longer a good way to talk about software.

So what is a domain-specialist, amateur programmer supposed to do? Learn all this dead-weight culture and legislation and ritual and still not be able to solve their problem? Or follow the promise of “coding made trivial” and spark up ChatGPT?

Ah, but there’s a problem. If programming languages are not a good way to talk about software, why would we imagine that English is any better? When programmers get together to discuss what to code, they draw diagrams with only occasional textual annotation (usually, not even English). Can you imagine defining a circuit diagram only in English? Or how about assembling Ikea furniture using only a verbal description? Even for navigating a road trip, most drivers prefer maps with occasional instructions, much more than a list of instructions without any visual structure. That, I think, is the reason why vibe coding doesn’t work. Quite aside from the fundamental limitations of LLM technology, the very idea of discussing a program in English seems wrong-headed.

Unfortunately, we can’t go back to the days of Visual Basic. Easy enough for amateurs it may have been, but being based on 1990s ideas of programming, it’s just as anachronistic in today’s world as C++, Java, and their descendants like Rust and Go. But if we could re-awaken the spirit of VB in a more modern style, surely that would empower the amateurs far more than vibe coding ever could.

We can see fragments of such an approach in today’s landscape. AWS, along with other cloud providers, offers serverless computing. They provide, like VB did, a palette of pre-fab services, which you wire together using simple glue logic. Their Cloud Development Kit means you can create your configurations offline, and then upload them to deploy them in one step. The promise is: you pay only for what you use.

But it’s not as simple as that. You have to specify everything: authentication, networking, data storage, backups, and all the other dozens of moving parts that a modern cloud deployment needs. To use serverless effectively, you don’t need to be an expert programmer, and you really don’t need to worry about provisioning the hardware, but you do need to be an expert architect, and that seems to be an even higher bar to clear.

It’s worse: Serverless only takes care of the back-end, cloud resident part: you still need to create a front end website or mobile apps, you still need to handle your own domain and certificates, there are odd restrictions on what you can do (so you still need to have one eye on the underlying machinery), and AWS being AWS, the costs are almost entirely unpredictable.

At the other end of the scale are Platform as a Service offerings such as Render. Their proposition is: all that complex machinery (networking elasticity, authentication, security, backups): those are pretty much the same on every application. What makes you unique is your software. So, you develop your code, upload it to their site, and they do everything necessary to make it run safely.

It’s a great service, but it isn’t a complete answer either. How do you make your software? Once you start to develop it, how do you architect it? (There’s still some basic architecture necessary.) And, once again, how do you develop the front-end?

Finally, there are any number of no-code website builders. Wix, Ionos, Zoho, Wordpress (and there are many others) allow anyone to build a website just by dragging and dropping pre-fabricated components. Each product has its own strengths and weaknesses — some are better at building shops, and others are better at building customer relationships. Some are primary-school simple, and some are a little more complicated. But all are designed, carefully, to be usable by new customers after only a minute’s worth of exploration.

Within their own terms, each is very effective. If what you want is an online shop, for example, Wix gives you everything out of the box. But what they don’t allow is any kind of innovation: they’ve solved one problem, and you’re just adding some branding and a little configuration on top of that. If you want to create a new product entirely, based on a custom mobile app for example, they really can’t help.

It’s painful to admit it, but the truth is: the vibe coders are right. Building software is far too difficult. Not only does that make it harder for amateurs to actualise their ideas, it’s actually making it harder for all of us professionals too — we waste inordinate amounts of time on activities that aren’t adding any value to our software, and we still make code that’s unpredictable and insecure (though, to be fair, not as unpredictable and insecure as vibed code!)

We need to begin building tools that, from their very first cough, speak to the fundamentals of modern programming: distributed execution, elasticity, concurrency, resilience, persistence — and UX as well. We need tools which guide their users, dialogically and incrementally (like VB used to) from trivial models of their task to finished artefacts.

The problem which vibe coding attempts to solve is real, non-trivial, and serious. It’s just the solution that sucks.

Written by Jules May

173 followers

16 following

A software developer and consultant specialising in reliability, compilers, and mathematical software. https://julesmay.co.uk

1 me gusta