AI 1: Lessons from vibe coding a major project | ||||
|---|---|---|---|---|
| I think this is a good analogy for how vibe coding falls apart at scale: Let’s say you’re a premiere vibe coder. You’ve got all the tricks up your sleeve — 200 AGENTS.md files, 50 Claude Code subagents, $200 a month for Opus 4, all that jazz. You want to make a to-do list app. You know how to write frontend and backend code but are too lazy to write the code yourself, so you pick a tech stack (React + Vite + Convex, which LLMs are particularly good at) and vibe. Your codebase is maybe ~600 lines total initially and looks like this:
It’s overall beautiful, but snuggled into a mutation or schema somewhere is a bad design choice. Probably won’t matter too much, especially when all of the code is beautiful! It’s only five lines of code, you can just go back and audit/remove it later. Time for your next feature, being able to have multiple lists. You continue vibing, adding a second layer to your app’s code. It now looks like this:
Okay, some parts now rely on the bad design choice so they also are a bit garbage. That’s fine though, it can’t spread too much. Or can it? 10 big features later and your app looks like this:
The garbage code now firmly sits in your codebase and even takes up a considerable chunk. Maybe you even wrote some of the green parts, but the poop is so omnipresent you’re scared to touch X, Y, and Z parts of your codebase. Even if you just want to change the type of animation in a microinteraction, you have no idea how to do so, and worry that having Claude do it will break the codebase. What should happen right now is, given all of your learnings from using the vibe coded prototype, you completely rewrite the app. Maybe you use AI assistance (Claude Code), maybe you even copy some of the green parts from the old codebase. But you actually look at and understand the code this time. When you want to change the color of an element, or the length of an animation, you don’t need to pray that Claude understands. You intimately understand the codebase so you can find and tweak the Tailwind class. What happened in my case is that we just kept on building. This ended up having a few side effects:
A genuine text I sent to the team is:
This is the story of a recent project that I contributed to, Dotlist1. Dotlist is still undergoing development and I have faith that we’ll figure out how to fix these mistakes. This isn’t meant to roast or dunk on our choices — instead, it’s a cautionary tale for how to avoid them in the future2. Eventually, you have to start fresh with a new codebase. This is what I did with Dotlist Lite. It’s not meant to compete with Dotlist, and I’ll probably only touch it sporadically from now on. I made it because I needed a good todo list app now, not when we figured out how to fix the UI of Dotlist. I specifically designed Lite to be much leaner while still supporting all of the important features, as well as much more polished microinteractions/UX. How I got this to happen while still using Claude for the most part is quite interesting:
In conclusion, vibe coding is good. Until it’s not. I still think the biggest advantage of vibe coding is to get an idea out super quickly — PMs can vibe code a prototype of certain functionality instead of trying to describe it to engineers, etc. The issues start emerging when using vibe coding to actually write the majority of a production grade application. If you’re looking for a simple todo app, check out Dotlist! It is still a great choice even if it’s vibe coded and the team is working hard to fix our issues with the AI-generated base. Footnotes#
|