I spent a year building AI features and shipped nothing
What over-engineering burnout actually looks like from the inside, and the ordering mistake that caused it.
I want to tell you about the most productive year of my life in which nothing shipped. I wrote thousands of commits and I ran coding agents around the clock. I redesigned the same dashboard so many times that I have folders of screenshots that look like a design agency’s entire portfolio. At one point I had four separate repositories that were all, if you squinted, the same product. And at the end of that year, the number of people who could visit a URL and pay me money was zero.
This is a story about burnout, but probably a different kind than you’re picturing. The exhaustion came from motion. There’s a specific fatigue that comes from being busy every single day while a quiet voice in the back of your head keeps asking why nothing is actually done, and it compounds worse than any crunch I ever worked in a real job.
The tools make that motion even easier to mistake for shipping. You can burn through serious agent usage before lunch and still have nothing a stranger can click. I once lost $60 to an agent left running, and the money was the small part. The bigger cost was how normal it felt to treat overnight agent churn as progress while the public URL still couldn’t take a payment.
How it actually happens
Nobody decides to over-engineer. It arrives in small, individually reasonable steps.
Mine looked like this. I started building an AI content-checking product. Sensible scope, real users waiting, an actual scoring engine that worked. Then I decided the database layer needed to be right before I went further, so I evaluated ORMs. I ended up with one ORM on the main branch and a different one on the development branch, with a decision document explaining which one would win. The document was well written. It was never executed. For months, every feature had to be mentally reconciled against two database layers, and so most features quietly didn’t happen.
Meanwhile the dependency count crept up. One app hit 72 packages. Every one of them had a justification at the time it went in, and collectively they meant that any security alert, any major version bump, any tooling change turned into an afternoon of archaeology. Agents are happy to add packages. They are less happy to delete them. The archaeology bill compounds quietly until a “quick” update takes half a day.
None of those packages got me closer to a stranger paying. Each one made the next change feel heavier, which made the next “fresh start” feel more rational, which made the fourth repo feel inevitable. That loop is how a working scoring engine turns into a museum of almost-products.
Then came the copies. When a codebase starts feeling heavy, there’s a powerful temptation to start fresh, and I gave in to it repeatedly. Fresh repo, better stack, lessons learned this time. Except the old repo never died, so now there were two. Then a prototype for a rebrand. Then a shell generated with one of the AI app builders to explore a new information architecture. Four codebases, each holding some fraction of the product, none of them deployable.
That last move deserves a sharper name. Vibe platforms and AI app builders are useful for prototyping a shell. You can describe a screen, get something clickable, and show yourself that the product could exist. The danger is treating that shell as another fresh start of the same product. A prettier empty house next door. You copy the idea into a new stack, feel the relief of a clean slate, and quietly multiply the unfinished copies. The shell is a sketch. It is not a second product lane, and it is not a reason to abandon the repo that already has auth and a billing spine half-built.
Here’s the part that took me longest to admit: all of that work felt great while I was doing it. Evaluating ORMs feels like engineering. Watching agents refactor code overnight feels like the future. The feeling of progress and the fact of progress had completely detached from each other, and I couldn’t tell, day to day, which one I was experiencing.
The new failure mode: review theater
There’s a newer version of the same detachment that I didn’t have a name for during that year, and I still fall into it: too much time on BugBot, CodeRabbit, and review theater, and not enough time in the browser as a user.
AI over-engineers. It will happily add abstractions, extra layers, and “robust” paths you did not ask for. Review bots will comment on all of it, sometimes helpfully, sometimes by inventing a second conversation about style while the checkout flow still double-submits. You can spend an afternoon clearing comments, merging clean diffs, and feeling like a careful engineer, then open the live site and discover that a stranger still cannot complete the one job that matters.
That is the same motion burnout in a new costume. Diff hygiene is not launch. Green checks are not a customer. Ship order is law for a reason: landing page, legal, pricing, payments, then features. Review bots sit downstream of that sequence. If the boring launch artifacts are missing, polishing the feature PR is rearranging furniture in a house with no front door.
The tell is where your attention goes after lunch. If you open another review thread before you open the product in an incognito window, you are protecting the diff instead of the user. I have done that more times than I want to admit. The PR looked serious. The live path still broke on the second submit.
I still use the review tools. I do not let them set the agenda for the day. The first question is whether a real user can reach a URL, understand the offer, and pay. If they cannot, the next hour belongs to launch artifacts or browser testing, not another round of bot comments.
The ordering mistake underneath all of it
When I finally sat down and audited the wreckage, the root cause turned out to be ordering rather than any single technical decision. I built the product before the launch.
The unglamorous artifacts that let a product actually take money, the landing page with real pricing on it, the terms of service, the privacy policy, the refund policy, the payment account, the domain wired to production, all of that sat at the bottom of every to-do list for a year. It was always “after the next feature.” And because the finish line was never wired up, no amount of feature work could ever reach it. I had built most of a paid product, including auth and a working billing spine, and I had never once been in a position where one more day of work would have produced a customer.
Flipping the order changed everything about how the work feels. Site first, legal pages, real prices in real currency, payment approval, and only then the feature lane. The day I made that flip, I found out my first revenue required zero feature building. Everything the first paying customer needed already existed. It had existed for months, buried under work that only I would ever see.
That discovery still stings a little when I remember it. Months of clever feature work had been optional. The blocking work was paperwork and wiring. The agents could have kept running forever and never closed that gap, because the gap was outside the repo.
The flip also changed what counted as a good day. A good day used to be a long agent transcript and a thick commit list. After the flip, a good day was a pricing page with real numbers, a privacy policy that matched what the product actually did, or a payment approval that finally went through. Those days felt smaller in the moment. They were the first ones that could end with money.
What I actually changed
Nothing fancy. I wrote the rules down where my tools and my future self would keep seeing them.
One lane. One repository is the product. Every other copy got archived or demoted to a reference I can read for design ideas. The AI app builder shell got the same treatment: useful as a sketch, demoted as a second product. The decision took an afternoon and removed a background hum of guilt I’d been carrying for a year.
Deletion before improvement. When a dependency has a vulnerability, the first question is no longer “what version fixes this.” It’s “is this package even used.” An unused package doesn’t get patched; it gets deleted. Around a third of the time, that’s the answer, and the same test works on features, branches, entire repos, and review-bot workflows that eat a day without moving a user closer to paying.
Ship order is law. Landing page, legal, pricing, payments, then features. I wrote it down as a sequence with no branches, because the branches are where I lose years. Browser-as-user testing sits inside that law. If I cannot click through the path a stranger would take, I am not ready for another feature lane. The ORM fork, the 72 packages, the fourth repo, and the review-bot rabbit hole all lose to that sequence. They can wait until someone can pay.
I still feel the pull. This week, while shipping the very site you’re reading, I caught myself wanting to build a contact-form backend before the site had a domain pointed at it. The instinct doesn’t go away. What changes is having something outside your own head that says: that’s not the next step, and you already know what the next step is.
If you’re a year into building and no stranger can pay you yet, more effort will make it worse. Put the boring launch artifacts first. Open the product in a browser like a customer would. Archive the extra repos. Let the review bots wait their turn. The relief on the other side is enormous.