This site is under construction - not everything may work correctly yet.

user@boten-dev ~/posts/done-beats-perfect $ cat README.md

Done beats perfect

2026-04-01 5 min read learning

There's a particular kind of paralysis that hits when you want something to be good. Well, not just working-good... Perfect. You start planning. You research the "right" way. You compare options. And at some point you realize you've spent two weeks comparing approaches and haven't written a single line of code.

I know this pattern well. I've been living it for years.

The perfectionism trap

I'd rate my own perfectionism at about a 6 out of 10 if the thing just needs to work. Closer to 8 if someone else is going to see it. Sounds manageable on paper, but here's how it actually plays out: strong enough to see every flaw, not strong enough to stop you from spotting new ones once you fix the old. The result isn't no work - it's a ton of work with surprisingly little to show for it.

At my day job I work with dbt. When I started, the repository situation was... complicated. Multiple interdependent repos that eventually got merged into a monorepo. Managing it all became painful enough that I started rewriting from scratch. And with a fresh repo came the need for a fresh CI/CD pipeline on GitLab.

The setup I inherited had a fun problem: it installed dbt from scratch on every single run, then wiped everything from the GitLab Runner after the pipeline finished - whether it succeeded or not. So if Redshift was having a bad day and one step failed, you couldn't just rerun that step. You had to start over. The whole thing. From the installation.

The first pipeline for the new repo was basically a 1:1 copy of the old one. I'm not even sure dbt build existed back then - we ran every step separately, and that's what I carried over. Mostly because I was still learning how GitLab CI/CD actually works. One repo instead of many subfolders, same logic, same problems.

It was bad. But it worked.

Iteration over architecture

That ugly first pipeline ran for weeks. During those weeks I learned what actually mattered: which steps were slow, which failed silently, what the team needed from CI. I couldn't have learned any of that from documentation alone. I also learned a lot from the engineer on the team whose responsibilities I was gradually picking up - having someone to absorb knowledge from made a huge difference.

Over the following months I replaced it piece by piece. The biggest win was replacing the per-run dbt installation with a proper setup in yet another new repository - this time with custom Docker images and their own tests. The pipeline today is fast, reliable, and honestly pretty clean. The full story of how I got there is a topic for another post.

But it exists because the first version was bad.

Pattern Ship something that works. Watch it break in ways you didn't predict. Fix those things. Repeat. The learning happens between versions, not before the first one. And there's something else - debugging your own mistakes teaches you better than any tutorial ever could. Especially when you feel that rush of finally cracking a problem after the nth attempt.

The same thing happened with my homelab. I installed CasaOS on an old PC with the thought that I can always wipe it and start fresh. That "temporary" setup has been running for over two years now. And here I am, wanting to move everything to better hardware, and... freeze. Because now the network has to be perfect, the configuration has to be perfect, every solution has to be the right one. The irony isn't lost on me.

Why "later" usually means "never"

I have a long list of things I want to build. A website (at the time of writing this post, still very much under construction). Learning to solder and remembering my electronics basics. A smart home that doesn't report to Google every time I turn on the bathroom light. An app that tracks game achievements.

For most of these, I spent months in the "research phase." Watching YouTube videos about the best approach. Keeping dozens of tabs open in my browser. Buying components and leaving them in a box for later. The pattern is always the same: enthusiasm spikes, planning starts, complexity reveals itself, and the project quietly joins the pile of shame - if you're a gamer you know exactly what I mean.

The shift came when I realized I was already doing the opposite at work - and it was working. The dbt model I optimized from 3000 seconds down to 100? The first version was hundreds of lines of CTEs chaining together models that each had a terrifying number of upstream dependencies. It was slow. But it produced correct numbers, and that bought me time to optimize properly over the following weeks.

At work, when something breaks, my priority is getting it running again - not making it perfect. Improvement is only possible if the thing works in the first place. But for personal projects? I was waiting for perfection before writing line one.

Good enough to exist

This site is a good example. Static HTML and CSS. No framework, no build step, no CMS. I'm an analytics engineer who writes SQL for a living - I don't need to learn Next.js to publish a blog post.

Is it rough? Yes. Am I already rebuilding it? ...yeah, guilty as charged. But it exists, and you're reading this on it right now. That's more than any of the "proper" versions I planned ever achieved.

The same applies to AI tooling. I didn't wait until I deeply understood prompt engineering before touching Cursor. I installed it, pointed it at my dbt repo, and started experimenting. The first rules I wrote produced models that technically ran but were wrong - bad style, bad configuration, bad everything. But each failure taught me something I couldn't have learned from reading docs. A month in, I have a system that generates dbt models in a consistent schema. It saves me hours every week - and it exists because I started before I felt ready.

Done as a practice

I'm not arguing against quality. That CI/CD pipeline I mentioned? It's good now. The dbt models are well-tested. But they got there through iteration, not through upfront design.

The mental model that works for me: the first version's only job is to exist. Some might call it a minimum viable product (hehe) - I just call it "something I can actually improve". It doesn't need to be good. It needs to be real - something you can point at, run, break, and learn from.

Honest note This isn't advice I always follow. I still catch myself researching for weeks instead of building for an hour. Writing this is partly a reminder to myself.

For someone with too many interests and a tendency to start things and not finish them, this is the difference between a portfolio of shipped projects and a browser with 47 open tabs.

The things I'm most proud of all started as the worst version of themselves. And they got better because they were there to improve.

Ship it. Fix it later. Or don't - sometimes the ugly version is enough.