How I lost control of my AI-coded app in just one day (and what I learned from it)
For over six months starting in Fall 2024, I barely wrote any code as I helped my wife build her business. I still followed tech news closely and heard a lot about recent advances in AI. In April, my brother finally convinced me to give Windsurf
a shot — it was a “free unlimited week” with GPT-4.1
.
The Project
As a paraglider pilot, I’d noticed an unmet need in the community: the ability to display thousands of .igc
flight logs (kind of KML
files for air sports) on a single map with 3D visualization. Performance issues had kept all existing multi-track tools from showing more than about 10 tracks at once. I’d already played around with the community’s APIs and identified some promising technologies to tackle this idea (deck.gl
to the rescue).
The Dream
I dove right in, started a small Next.js
project with no backend. I brought in a folder containing some of my old experimental code on the topic. After feeding Windsurf (GPT-4.1)
the context, I went all in. Very quickly I had something running, something even displaying.
I’d promised myself I’d only spend one exploratory afternoon. I forbade myself from writing a single line of code. I only worked through prompts. Very (too) quickly, I kind of stopped reading the code; I was just focusing on the results.
Eventually, the “vibe-coding” session that lasted late into the evening, uninterrupted. A nearly-forgotten sensation washed over me — I hadn’t experienced such intense focus on code since my last startup’s early days. That night, I managed to display thousands of tracks on this single map.
A first version of my project was ready. In the days that followed, I used the app to create some beautiful maps of our local paragliding competition, which I shared on a community page I was running.
The Disillusionment
But a few weeks later, the free Windsurf
trial ended. I needed to add a tiny new feature and realized some things I thought were working actually had bugs. I was no longer “hands-on” in the code, so it wasn’t a good time to subscribe to Windsurf or any service. Besides, I only wanted to change my month selector to a date-range selector for more precision.
Then I re-opened the code, and reality hit me like a cold shower:
- Dead code: left over from features I’d initially requested and then changed later
- Messy structure: everything’s in one place, not properly broken down
- Huge files: monolithic and lacking abstraction
- Unnecessary complexity: sprinkled throughout the code
Take this if-statement — it’s not obvious at a glance when it actually triggers:
export async function parseIGCAsync(
arrayBuffer: ArrayBuffer,
resolution: number,
context?: unknown
): Promise<number[]> {
let text: string;
if (
context &&
typeof context === 'object' &&
'loader' in context &&
context.loader &&
typeof (context as { loader: { decodeText: unknown } }).loader.decodeText === 'function'
) {
text = (context as { loader: { decodeText: (buffer: ArrayBuffer) => string } }).loader.decodeText(arrayBuffer);
} else {
text = new TextDecoder().decode(arrayBuffer);
}
return parseIGC(text, resolution);
}
And that’s when it clicked: this wasn’t a viable way to “vibe-code”. What I’d done was closer to “trash-coding”: results that looks shiny on day one but collapses the second you touch it.
Above all, I had no clue how to properly implement my new little feature. I spent way too much time on it — something that would have taken far less effort with a good architecture and at least a basic understanding of the codebase. The dead code only made the pain worse.
The Moral of This Story
I’m glad I got to learn this lesson on a fun side project rather than something mission-critical. It taught me not to repeat the mistake of charging in headfirst and letting the AI dictate my architecture without oversight.
Because “vibe-coding” doesn’t have to mean garbage-code. As I argued in my earlier post (Read That F*cking Code
), the key is learning to channel it: building fast while keeping control/sight, so the code can actually live beyond day one.
Not long after, I discovered Claude Code
and started a new project with an architecture I already controlled (a clean-architecture setup). From the very first lines of code, I was careful to guide Claude with clear instructions and to correct the course whenever needed. It turned out to be far more efficient, and coding has never been so much fun!
“Vibe-Coding” isn’t “Trash-Coding” — unless you let it be.