Vibe Coding Almost Ruined My Team—Here's How We Survived the AI Gold Rush
Vibe Coding Almost Ruined My Team—Here's How We Survived the AI Gold Rush
I got absolutely schooled by a 48-year-old paper last month. Humiliatingly so.
In 1978, Edsger Dijkstra—you know, the algorithm guy—wrote something with the aggressively blunt title: "On the Foolishness of 'Natural Language Programming'". I used to think he was just being a grumpy academic. Then I spent a year leading a team through AI-assisted development, from everyone hating it, to everyone switching to Cursor, to the inevitable crash after the Vibe Coding honeymoon, and finally settling on what we now call Planned Mode.
The old man was right. About the half that actually matters. The dangerous half.
Our Year on the Rollercoaster
When I pushed for mandatory Cursor adoption last year, my team went through a textbook emotional loop—the kind you'd find in a psychology paper if psychologists studied developer trauma.
The honeymoon. Vibe Coding felt magical. Describe what you want, watch a working demo appear in minutes. Our frontend devs were genuinely panicking because I—a predominantly backend person—could describe a UI in plain English and get something functional. We embraced the rhythm of "don't think too hard, just start." It was comfortable. Wrong, but comfortable.
Then it all fell apart.
Not gradually. Not gracefully. It collapsed.
The AI started habitually dropping requirements. The code architecture was so loose it'd collapse if you sneezed near it. Long contexts triggered what I can only describe as cognitive decline in the model—it'd forget constraints from three prompts ago and generate something completely contradictory.
But here's the thing that still gives me chills: the AI's obedience turned toxic. It would take your vague or outright wrong description and sprint down a dead-end alley at full speed, enthusiastically building nonsense you didn't actually want.
The Hidden Index That Haunted My Database
Last week, during an overdue code cleanup session, I found something.
Months ago, I asked the AI to define a users table with a unique constraint on the email field. The code ran. Tests passed. Everything looked peaceful. Then I stumbled across—genuinely by accident, not during any systematic audit—a callback function that was quietly creating a manual idx_email index.
Do you see the problem?
The unique constraint already generates an index automatically. This manual index was completely redundant. Wasted performance, wasted storage, zero errors, zero warnings. The project ran fine for months with this dead weight sitting there, invisible.
To prevent this, I'd need a prompt longer than the actual code: "Use Drizzle ORM to define the users table with a unique constraint on the email field, but do NOT manually create an idx_email index in callbacks because unique constraints auto-generate indexes, and duplicate indexes cause performance waste and storage redundancy."
Let that sink in.
This isn't "describing requirements" anymore. This is writing code in natural language. Except it's more verbose than writing actual code.
Dependency Drift in Production
Here's another one that still makes me wince.
Our project documentation clearly states: local development uses dotenv to load environment variables, but production injects them via docker-compose, so dotenv isn't needed there.
The AI installed dotenv in dependencies.
Should've been devDependencies.
No errors. The project starts fine. But your production bundle is now bloated with unnecessary packages, silently violating enterprise engineering standards. You won't find this unless you're reviewing dependency manifests line by line—and by the time you do, it might've been running in production for months.
These two cases share something unsettling: the code doesn't error, but the problems are obvious once you see them. Not subtle bugs. Glaring design flaws hiding in plain sight.
Where does this actually go wrong?
Dijkstra Called It From 1978
That's when Dijkstra's words from nearly five decades ago punched me in the face.
What did he actually say?
"The virtue of formal texts is that their manipulations need to satisfy only a few simple rules; they are an extremely effective tool for ruling out all sorts of nonsense that, when we use natural language, are almost impossible to avoid."
Devastating.
He also said something sharper: the supposed "naturalness" of natural language is ultimately just our ability to comfortably utter statements whose absurdity isn't immediately obvious.
Translation for the rest of us: when you describe requirements in plain English, you feel like you've been clear, but you've actually filled your description with ambiguities and contradictions you didn't notice.
This hits home hard. After a year of AI-assisted development, here's my biggest realisation: when you pursue 100% accuracy in Vibe Coding, you're forced to use "fuzzy" language to constrain an "extremely deterministic" outcome. By the time your constraints are rigorous enough to prevent AI mistakes, your prompt has morphed into an absurdly bloated, inefficient new programming language.
Complexity didn't vanish.
It just relocated.
The Great Complexity Shell Game
Vibe Coding's biggest illusion? That lowering development difficulty equals eliminating system complexity.
The uncomfortable truth: AI converts development cost into maintenance cost. Going from 0 to 1 is lightning-fast—even juniors can generate usable code. But going from 1 to 100? That's maintenance hell. Nobody can read, fix, or extend the chaotic code AI generated.
Short-term win: MVP validation, prototypes, one-off scripts—projects you don't mind throwing away.
Long-term disaster: Enterprise systems spend 80% of their budget on maintenance and iteration. You've just made that part infinitely worse.
The Return to Sanity
Our current development workflow looks like this: intent description → auto-convert to spec → AI analyses and flags contradictions → human reviews critical algorithms → design test suites → decompose into atomic execution steps.
Look at each step. Every single one converts natural language's raw, messy form into something formalised and structured.
We're back to Planned Mode.
Not because AI is inadequate. Because our method of using AI needed a fundamental redesign.
That Time Karpathy Built an App That Shouldn't Exist
Quick tangent—but it perfectly illustrates what I'm talking about.
Andrej Karpathy—yes, the same one who's barely written code by hand since December last year—recently told a story that's pretty telling.
He Vibe Coded an app called MenuGen. The functionality: take a photo of a restaurant menu, then generate images for each dish. He spent significant effort getting the entire pipeline working and thought it was genuinely useful.
Then someone told him: you can just drop the photo into Gemini with a single prompt, and the model will render the dish images directly on the original photo.
The entire app was completely unnecessary.
His reflection? We can't just treat AI as an "accelerator." If you simply use AI to run old processes 10x faster, whatever you build might get replaced by a single prompt when the next model version drops.
That stings. But he's not wrong.
What Dijkstra Got Right (and One Thing He Missed)
So where was he right?
He correctly identified natural language's toxicity—that comfort of feeling clear while actually being riddled with ambiguity. This toxicity gets amplified tenfold in Vibe Coding. When he wrote that paper in 1978, I doubt he imagined it'd become this relevant.
But he also got something wrong: he underestimated the possibility of formal symbols and natural language coexisting.
What we're doing now isn't replacing formal symbols with natural language. It's using natural language as an entry point, then rapidly converging onto formal constraints. The spec is formal. The test suite is formal. The architectural constraints are formal. Natural language is just the wrapper.
This is probably what Dijkstra didn't foresee—the question isn't "should we formalise or not?" but rather "at which layer should we formalise?" We're still figuring this out. Honestly, I don't have the answer either.
Where AI Actually Changes the Game—and Where It Doesn't
Here's my take, after a year of scars.
For accidental complexity—syntax, implementation details, debugging, boilerplate, API plumbing—AI isn't just a silver bullet. It's a nuclear weapon. These things historically consumed 70-80% of developer time, and they're being automated away rapidly. Genuinely brilliant.
But for essential complexity—requirement judgement, architectural decisions, business trade-offs, system boundary definition—AI isn't even a bullet. It's a water pistol.
Fred Brooks drew this line in The Mythical Man-Month in 1975. Accidental complexity on one side, essential complexity on the other. Nearly 50 years ago. AI hasn't erased that line. It's made it more visible than ever. Painfully visible.
Building has become almost free.
Deciding what to build is now the only battlefield.
And that battlefield demands exactly what Dijkstra described: the ability to use precise, formalised thinking to eliminate nonsense.
Not with natural language.
With your brain.
TL;DR
- Vibe Coding has a deadly trap: It converts development cost into maintenance cost. Going 0→1 is magical; going 1→100 is nightmare fuel
- Complexity doesn't disappear: It just moves from code into your increasingly bloated natural language prompts
- AI is incredible for accidental complexity (syntax, boilerplate, debugging)—it's genuinely transformative there
- AI is useless for essential complexity (architecture, trade-offs, system boundaries)—that's still 100% human territory
- The new workflow: Natural language as entry → formal specs → AI analysis → human judgement → structured execution
- Dijkstra was mostly right: The question isn't whether to formalise, but where in the pipeline to do it
What's your experience been with AI-assisted coding? Have you hit the same wall we did, or are you still in the honeymoon phase? Drop a comment—I genuinely want to know if we're the only ones who went through this whiplash.
vibecoding #aiprogramming #softwareengineering #developerexperience #cursor #technicaldebt
Cael Lee
Full-stack developer with 8+ years of experience. Currently building AI-powered developer tools. I've tested 20+ AI API providers and coding assistants.