Coding with AI: Brilliant, Infuriating, Unavoidable
If you’ve read my previous blog posts you’ll know that I recently returned to software development after a 10 year break. Expecting the process to be far more complex than it was when I first started 26 years ago, I planned for a reasonably steep learning curve... and was not disappointed.
My experience: beginning with the distant memory of fifteen years of LAMP stack development plus a ten year "break" from programming then re-starting from scratch with a two month campaign including a brand new laptop, 250+ hours of effort, close to twenty unfamiliar tools, languages, libraries and processes and five different AI models, to delivering one production-ready React/FastAPI SaaS application, I think it's safe to say that I have learned quite a lot. Let me tell you about it.
Strictly Speaking
One of my criteria for re-building my programming skills is that I want to produce code that I can show to other people and not be ashamed of. My last app, while it may have been impressive to my clients, was a mess under the hood. Not so much because I was a bad programmer, but more because I was almost completely unaware, or possibly unconcerned with things like strict variable typing, form validation, input sanitizing, database optimization, URL routing, DRY principles, separation of concerns, technical debt and, worst of all, data security.
Despite the successful app it delivered, I would be embarrassed to have anyone see that codebase... so I made a “team agreement” to not only use proper code repo and branch development processes, but also to be mindful of security, adhere to TypeScript, perform frequent code reviews and optimization passes and implement unit and performance stress testing, among other things.
These were the tools and processes I expected to spend the most time learning how to do properly but, to be honest, they turned out to be the easy part.
The Actual Hard Part
While it is still incredibly valuable to have a strong grasp of the fundamentals of programming, it turns out to be a lot less important to the actual process of creating an application than you might think. Just like it's possible for a novice to learn to drive most cars without any knowledge of how an internal combustion engine works, using a modern IDE (integrated development environment for you non-coders) largely frees you from having to worry about managing the complex array of tools, languages and processes that go into building a React / FastAPI code base.
Even if you're writing every single line and character from scratch (and how many people do or have ever done that, really?), I was surprised at how much easier it is to produce and manage working code using an IDE like VSCode than it was for me to do it using BBEdit and SFTP twenty-six years ago. It turns out that all the “hard stuff” like proper formatting, version control and debugging are easy when you’ve got the right tools... and it makes even some of the actually hard things, such as branch management, database migrations and CI-CD, somewhat easier than they used to be.
Despite the incredible improvements in code creation and deployment tooling, you hear very little discussion about them in the general press. I mean, what venture capitalist is really going to get excited about being able to see stack traces in real time when you have a component forcing unwanted display re-renders? What everyone is impressed about, of course, is how "AI" has made it super-easy to "be a coder" and that the industry is "primed for disruption" in light of this recent "technological advancement".
"AI is great!" they say... "You can use AI to create an entire program in a single prompt!" they tell me... but after what I've seen these past two months, I have to wonder where they're getting their news from. It has been my experience, contrary to popular beliefs about AI, that the truly difficult part of modern programming is not the programming itself and not even the tools one uses for programming, but rather trying to integrate generative coding "AI" models effectively... not because they're complicated, mind you, but because they're dumb and because working with them will make you want to darksydephil your mouse and keyboard against a wall (trigger warning: lots of cursing and senseless destruction of innocent electronics).
It's Potatoes All The Way Down
If you're familiar with the popular video game series "Portal", you'll remember the plot twist where the game's seemingly evil and probably psychotic AI "GLaDOS" was downloaded into a computer running on a potato, turning it into a comical and almost sympathetic side-kick. For all the doom and gloom about how “AI is coming for programmers” I have to admit at this point that I’m about as scared of the coming AI programmer apocalypse as I was about that pathetically potato'ed super-villain.
Well, to be honest, I never really was worried about "AI" because I know how it actually works... but now that I’ve spent over 250 hours partner programming with it in a sustained and highly focused manner, I can tell you I’m not concerned for programmers at all. I’m actually far more concerned for the AI... with the caveat that even while in potato form, GLaDOS was capable of actions that were quite vexing and often, at least for the game's protagonist, potentially deadly.
Having said all of that, Let me share with you the top five things I learned about coding with AI over the last two months.
5) The Meeseeks Effect - The fictional “Mr. Meeseeks” from the Rick & Morty cartoon is an infinitely wise and capable being conjured instantly out of nothing which exists solely to perform a task before happily vanishing. For a Meeseeks, existence is painful... so the longer it takes for them to complete their assigned task, the more suffering a particular Meeseek experiences, and the more frustration, anger and potential violence it's "master" has to endure.
An AI’s context window functions much the same way. Unlike ChatGPT, which appears to have “infinite context” due to some clever "memory" management, coding tools like Claude and Codex suffer from painfully limited context windows. As an instance of one of these tools gets “older”, its limited context window begins to fill up and it gets harder and harder to work with... to the point where you find yourself wrestling with trying to eek out one more bit of useful code versus having to explain the project to another brand new instance before finally realizing you've wasted hours going well past the point of diminishing returns.
4) Rubber Ducks - I once had a boss who casually mentioned she liked rubber ducks. Within weeks of this info drop, she had received a remarkable accumulation of rubber ducks of every shape, size and style from all the friends, colleagues and workers who jumped on the opportunity to offer an "easy to choose" gift. Despite her office becoming nearly full to overflowing with ducks since then, and regardless of what she said or did, the rubber ducks kept coming... including the one I gave her myself.
One of the things that happens to AI over time is a tendency to fixate on particular concepts or patterns. Claude Code seems to really love to apply certain kinds of logic structures like state watchers that wait for a condition to become true before performing an action, for example, over callbacks which are explicitly told when an action is completed so that they can trigger the next action. When AI gets obsessed with a concept or pattern, it will insert these “rubber duck” elements into your code without being asked... even despite being told not to... and there really isn't much you can do to stop it short of closing the instance and starting over again.
3) Toddler Breakfast - Have you ever had a five year old make you breakfast? Their desire to please you is matched only by their inability to produce edible food without creating a disastrous mess... so when they do bring you a plate that looks like a stack of fluffy pancakes smothered in maple syrup, you have to remember to check that the syrup isn’t motor oil and look below the first pancake to make sure the rest aren't cut out of cardboard. The kids aren’t trying to destroy the kitchen or send you to the hospital, they just care more about how the output looks and how they hope you'll feel about it than they do about the process... or the consequences.
It’s a constant battle to get an AI to produce the particular style or methodology of code you want to use, even if you have existing, well-defined patterns and repeatedly instruct it to review and replicate working code. Like that well-meaning kid in the kitchen, it almost seems like it’s training is biased toward the worst coding methods available in a given language, again not because it's bad, but because taking shortcuts and focusing on appearances is doomed to involve poor decision-making. And it’s not not a surprise you find this behavior in AI when you realize it was trained on not only all of the “good” code that exists on the internet but also all the “bad” code written by humans who do a lot of copy-paste of their own (trust me, I know) and have a tendency to fall back on well-worn techniques or quick patches to make things work in the short term.
2) Confident Contradiction - I was about three days into coding with Anthropic’s Claude when I realized I was spending a lot of time explaining what my codebase was and how I liked to work... so I created a file with my “directives” in it and just told the AI to read it first every time I started a new instance. About a week later, Anthropic updated Claude Code to feature a “CLAUDE.md” file which was designed to give structured directives automatically to each new instance of Claude. I like to think I was ahead of the curve on that one, a rare thing these days.
Anyway, it works... if by working you mean the new instance reads the file and can quote it... but the truth is that Claude will violate those directives almost immediately. It violates directives, acts without confirmation, uses dangerous tools (like SED, which is the text editing equivalent of doing real-time gene splicing with a chainsaw) which I have told it not to use, ignores requests, creates coding plans and then doesn’t follow them, agrees with my analysis of a problem then goes off and creates some entirely different solution, applies problematic coding methodologies I've explicitly warned against (like useEffect) and, worst of all, persists in communicating in ways that I find infuriating. And you can’t make it stop. Open AI's Codex, as another example, seems even more difficult, bordering on appearing arrogant, and will confidently contradict you, even when you’re right, and then ignore it when you point it out... like a dog refusing to look at its own fresh poop on the newly installed carpet.
1) Vibe Coding - The four things I've mentioned above range from mildly amusing to rage-inducingly stupidity, but the single most shocking thing to me about AI coding isn’t the AI, it’s the seemingly blind enthusiasm of some of the people who use it. I have a friend who claims to “vibe code” and when I asked him to explain his work process, he told me that he simply asks his AI tool to make something for him in a sort of “one shot request” and then if it appears to work, he accepts it as-is. No code review, no debugging, no optimization... if it doesn’t work the first time, just ask for it again until it does and never even bother to look at the code, he recommends.
Imagine, if you will, a Star Trek replicator where you're hoping for a nice piece of cake and it produces a turnip... and then a tire iron... and then a ball of yarn. Or worse, it gives you something that looks like a slice of cake but is actually a dirty dish sponge held together by drywall mud. And heaven forbid you should use vague words like “Tea, Earl Gray, Hot” and its idea of hot is the temperature of the surface of the Sun. It's not even about being unclear about what you want or experimenting with different approaches... that's what prototyping tools are for... it's about just "flinging a wish into the ether" and hoping the AI will, after burning a few hundred dollars worth of compute resources that you're getting for just $20 a month, produce something magical.
At that point you're not even "not coding" any more. If coding an app is like creating a full Thanksgiving dinner for 20 people where everything comes out perfect and arrives at the dinner table at the same time, "vibe coding" is like walking into a restaurant, asking for something "delicious without meat in it" and then throwing away 9 of the 10 dishes you receive... AND the chef is a state-of-the-art robot that costs $10,000 an hour to operate.
Checksum - AI Stinks in a Love-able Way
From the “confident contradictions” to the painful discoveries of actual code that completely diverges from planned work, the clearly voiced expectations that are met with complete non-sequitur responses, and the very real propensity for it to destroy the parts of your code that DO work by obsessing over the parts that it can’t figure out, it’s almost like working with AI is like, well, working with a human... a very powerful, enthusiastic and exceptionally childlike human who doesn’t understand the words “acceptance criteria” and who will just as happily piss on you as smile at you, sometimes doing both at the same time.
There is a silver lining, though; AI will do all of these things with a crisp, cheerful attitude, eternally promising and supportive... no matter how bad things get. And then when you inevitably find yourself typing curses at it, rage quitting and pulling the plug on it, you can just start a new instance and it will spring back to life all shiny and new with a bright “Hi! What can I help you with today?” It may be "frustrating" but it's chipper... and when it works, which is more often than you would think, it's brilliant.
Try doing that with an actual human.