This one works for me, and I've learned it from a post on HN. Whenever I feel stuck or overthink how to do something, just do it first - even with all the flaws that I'm already aware of, and if it feels almost painful to do it so badly. Then improve it a bit, then a bit, then before I know it a clear picture start to emerge... Feels like magic.
> Whenever I feel stuck or overthink how to do something, just do it first - even with all the flaws that I'm already aware of, and if it feels almost painful to do it so badly. Then improve it a bit, then a bit, then before I know it a clear picture start to emerge... Feels like magic.
Funny how these things when done by a human is a positive and when done by an LLM is a negative. According to all the anti-llm experts... Humans generate perfect code on the first pass every time and it's only LLMs that introduce bad implementations. And this isn't a callout on this user in specific. It's a generalization to the anti-ai sentiment on HN. If incremental improvement works, incremental improvement works.
I always try and keep in mind that we typically think of software as having three versions -- alpha, beta, and release -- but for it's considered even kind of "finished."
In my own work, this often looks like writing the quick and dirty version (alpha), then polishing it (beta), then rewrite it from scratch with all the knowledge you gained along the way.
The trick is to not get caught up on the beta. It's all too tempting to chase perfection too early.
I used to think this. Then I noticed how often "preparation" became its own infinite loop.
At work we built something from a 2-page spec in 4 months. The competing team spent 8 months on architecture docs before writing code. We shipped. They pivoted three times and eventually disbanded.
Planning has diminishing returns. The first 20% of planning catches 80% of the problems. Everything after that is usually anxiety dressed up as rigor.
The article's right about one thing: doing it badly still counts. Most of what I know came from shipping something embarrassing, then fixing it.
I agree that planning has diminishing returns, yet simultaneously nearly every software project I’ve been part of has been under-planned and ended up worse off for it.
I kinda agree, but I also gain pleasure from doing all those things that are not supposed to be "the thing". The thinking, the dreaming, the visualizing... I just like that. I do it a lot when working on personal projects (which some of them I never ship). I think it's fine, and I wouldn't go as far as saying that those things are "not doing the thing"; in many ways those things are "the thing", at least for me.
A lot of tech-savvy people (like me) love solving meta-problems.
Doing the thing that would take 10 minutes? Na man, let me build an unnecessary complicated technical solution that in theory enables hundreds of people to do the thing much more efficiently in just 2 minutes. That takes a month and the thing has not been done, ha.
In "Remains of The Day" they call just talking about "the thing" an indulgence. Which is really what it is, it feels good, isn't hard, and doesn't achieve anything.
The characters in the book are quick to cut non-productive discussions short, but it feels like the feel good discussions around "the thing" are about as far as many people want to go these days.
A bit of a meta lesson for me here: Writing a short, pointed, opinionated blog post is blogging. If I care about blogging my thoughts, I need to just do it, not worry about rigor or depth ahead of time
No it's not. Sometimes (or maybe most of the time) doing it badly means maybe it's not your thing.
I used to have a neighbour who liked to play the piano and sing. He was doing it consistently badly and he didn't have anyone to tell him that he should probably stop trying.
Oh.. So you start doing something new and you're top 10% without practicing or being bad at it first? I'd love to test that to see if it's the case... Your logic is "You're not the best ever to do something so you are not doing it" means you have probably never done a single thing your entire life. Maybe you should just stop.
On the other hand: sometimes doing the thing is itself a bad idea. One reason I continue to insist on design docs and code review is that I'd rather find this out ahead of time rather than deal with the damage afterwards.
In the GenAI era, "doing the thing badly without planning" has become so easy that some counterweight is needed.
At a previous company we used to joke that most of management was a "problem admiration society":
They'd love to talk about problems, investigate them from all angles, make plans on how to plan to solve the problem, identify who caused it or how to blame for it, quantify how much it costs us or how much money we could make from solving it, everything and anything except actually doing something about it.
That definitely happens, but I wish had the displeasure of working at companies that were enamored with the solution they have, and couldn't be convinced to look again at the problem and see how it's changed since they originally solved it. As with most anything, the best approach is to somewhere in the middle, combining a love for the problem with a drive to repeatedly solve it. And one of the best tools for that seems to be dog-fooding, when the people in the company really want to use it for themselves.
My nitpick is that thinking and dreaming about solving the problem is part of doing. Its the planning phase. Skipping This planning phase in Software engineering is the root cause of most Day 2 operations issues. However I agree that thinking or announcing about outcome is not doing.
I wholeheartedly agree. In an age of talking heads. you will not hear from the people actually doing the thing. because they too busy doing the thing versus talking about it. now excuse me ima go back to doing the thing.
Yeah; this is such a hard intuition to teach beginners. And something I think will be lost as we move more and more toward vibe coding.
There is so much to be learned about a problem - and programming in general - by implementing stuff and then refactoring it into the ground. Most of the time the abstractions I think up at first are totally wrong. Like, I imagine my program will model categories A, B and C. But when I program it up, the code for B and C is kinda similar. So I combine them, and realise C is just a subset of B. And sometimes then I realise A is a distinct subset of B as well, and I rewrite everything. Or sometimes I realise B and C differ in one dimension, and A and B in another. And that implies there's a fourth kind of thing with both properties.
Do this enough and your code ends up in an entirely unrecognisable place from where you started. But very, very beautiful.
There is a difference between shipping something that works but is not perfect, and shipping something knowingly flawed. I’m appalled at this viewpoint. Let’s hope no life, reputation or livelihood depends on your software.
> What surprised me was how much the ugly first version taught me that planning never could.
Fred Brooks, author of “The Mythical Man Month” wrote an essay called “Plan to Throw One Away” in 1975.
He argues much what you’ve described.
Of course, in reality we seldom do actually throw away the first version. We’ve got the tools and skills and processes now to iterate, iterate, iterate.
While I do agree with the content, this tone of writing feels awfully similar to LLM generated posts that flood some productivity subreddits recently. Are there really people who "spend weeks planning the perfect architecture" to build some automation tools for themselves? I don't buy that.
Commenter's history is full of 'red flags':
- "The real cost of this complexity isn't the code itself - it's onboarding"
- "This resonates."
- "What actually worked"
- "This hits close to home"
- "Where it really shines is the tedious stuff - writing tests for edge cases, refactoring patterns across multiple files, generating boilerplate that follows existing conventions."
I guess the important (and hard) part is to not make a categorical error and mix up design of high level functionality and UI with the plumbing underneath it.
The plumbing also needs iteration and prototyping, but sound, forward looking decisions at the right time pay dividends later on. That includes putting extra effort and thinking into data structures, error handling, logging, naming etc. rather earlier than later. All of that stuff makes iterating on the higher levels much easier very quickly.
For my personal projects, which are under zero time constraints, I usually build an ugly version, to figure out the kinks. Then delete it and write a proper one using the lessons I learned the first time.
I've forgotten where I've seen this now, but one of the best developers I've seen wrote code by writing it, deleting everything, then writing it again, sometimes many times in order to get their final code. I found it fascinating.
I completely agree and went by the proverb "everything worth doing is worth doing poorly" about a year ago now, it took some time for it to sink in but now I'm actually productive. My main blocker was waiting for other's approval, now I feel a lot more free.
This nails my issue with systems design insanity. There are so many things you learn through living with systems that are correct, though counterintuitive.
Do a thing. Write rubbish code. Build broken systems. Now scale scale. Then learn how to deal with the pattern changing as domains specific patterns emerge.
I watched this at play with a friend's startup. He couldn't get response times within the time period needed for his third party integration. After some hacking, we opted to cripple his webserver. Turns out that you can slice out mass amounts of the http protocol (and in that time server overhead) and still meett all of your needs. Sure it needs a recompile - but it worked and scaled, far more then anything else they did. Their exit proved that point.
> The hardest part is giving yourself permission to ship something you know is flawed. But the feedback loop from real usage is worth more than weeks of hypothetical architecture debates.
Nice statement.
I think there is another equally pervasive problem: balancing between shipping something and strategizing a complete "operating system" but in the eyes of OTHER stakeholders.
I'm in this muck now. Working with an insurance co that's building internal tools. On one had we have a COO that wants an operating model for everything and what feels like strategy/process diagrams as proof of work.
Meanwhile I am encouraging not overplanning and instead building stuff, shipping, seeing what works, iterating, etc.
But that latter version causes anxiety as people "don't know what you're doing" when, in fact, you're doing plenty but it's just not the slide-deck-material things and instead the tangible work.
There is a communication component too, of course. Almost an entirely separate discipline.
I've never arrived at acceptable comfort on either side of this debate but lean towards "perfect is the enemy of good enough"
The most important aspect of software design, at least with respect to software that you intend not to completely throw away and will be used by at least one other person, is that it is easy to change, and remains easy to change.
Whether it works properly or not, whether it's ugly and hacky or not, or whether it's slow... none of that matters. If it's easy to change you can fix it later.
Put a well thought out but minimal API around your code. Make it a magic black box. Maintain that API forever. Test only the APIs you ship.
78 comments
[ 0.22 ms ] story [ 89.1 ms ] thread1. https://strangestloop.io/essays/things-that-arent-doing-the-...
2. https://news.ycombinator.com/item?id=45939431
This one works for me, and I've learned it from a post on HN. Whenever I feel stuck or overthink how to do something, just do it first - even with all the flaws that I'm already aware of, and if it feels almost painful to do it so badly. Then improve it a bit, then a bit, then before I know it a clear picture start to emerge... Feels like magic.
Funny how these things when done by a human is a positive and when done by an LLM is a negative. According to all the anti-llm experts... Humans generate perfect code on the first pass every time and it's only LLMs that introduce bad implementations. And this isn't a callout on this user in specific. It's a generalization to the anti-ai sentiment on HN. If incremental improvement works, incremental improvement works.
In my own work, this often looks like writing the quick and dirty version (alpha), then polishing it (beta), then rewrite it from scratch with all the knowledge you gained along the way.
The trick is to not get caught up on the beta. It's all too tempting to chase perfection too early.
At work we built something from a 2-page spec in 4 months. The competing team spent 8 months on architecture docs before writing code. We shipped. They pivoted three times and eventually disbanded.
Planning has diminishing returns. The first 20% of planning catches 80% of the problems. Everything after that is usually anxiety dressed up as rigor.
The article's right about one thing: doing it badly still counts. Most of what I know came from shipping something embarrassing, then fixing it.
From the Red Dwarf book and quoted previously:
https://news.ycombinator.com/item?id=28033747
I needed this today. Currently questioning my career choices, as I hit my first wall where people are involved. Gave me quite the headache.
The characters in the book are quick to cut non-productive discussions short, but it feels like the feel good discussions around "the thing" are about as far as many people want to go these days.
No it's not. Sometimes (or maybe most of the time) doing it badly means maybe it's not your thing.
I used to have a neighbour who liked to play the piano and sing. He was doing it consistently badly and he didn't have anyone to tell him that he should probably stop trying.
In the GenAI era, "doing the thing badly without planning" has become so easy that some counterweight is needed.
They'd love to talk about problems, investigate them from all angles, make plans on how to plan to solve the problem, identify who caused it or how to blame for it, quantify how much it costs us or how much money we could make from solving it, everything and anything except actually doing something about it.
It was never about doing the thing.
There is so much to be learned about a problem - and programming in general - by implementing stuff and then refactoring it into the ground. Most of the time the abstractions I think up at first are totally wrong. Like, I imagine my program will model categories A, B and C. But when I program it up, the code for B and C is kinda similar. So I combine them, and realise C is just a subset of B. And sometimes then I realise A is a distinct subset of B as well, and I rewrite everything. Or sometimes I realise B and C differ in one dimension, and A and B in another. And that implies there's a fourth kind of thing with both properties.
Do this enough and your code ends up in an entirely unrecognisable place from where you started. But very, very beautiful.
https://wiki.c2.com/?PlanToThrowOneAway
There is a difference between shipping something that works but is not perfect, and shipping something knowingly flawed. I’m appalled at this viewpoint. Let’s hope no life, reputation or livelihood depends on your software.
Fred Brooks, author of “The Mythical Man Month” wrote an essay called “Plan to Throw One Away” in 1975.
He argues much what you’ve described.
Of course, in reality we seldom do actually throw away the first version. We’ve got the tools and skills and processes now to iterate, iterate, iterate.
Commenter's history is full of 'red flags': - "The real cost of this complexity isn't the code itself - it's onboarding" - "This resonates." - "What actually worked" - "This hits close to home" - "Where it really shines is the tedious stuff - writing tests for edge cases, refactoring patterns across multiple files, generating boilerplate that follows existing conventions."
The plumbing also needs iteration and prototyping, but sound, forward looking decisions at the right time pay dividends later on. That includes putting extra effort and thinking into data structures, error handling, logging, naming etc. rather earlier than later. All of that stuff makes iterating on the higher levels much easier very quickly.
Do a thing. Write rubbish code. Build broken systems. Now scale scale. Then learn how to deal with the pattern changing as domains specific patterns emerge.
I watched this at play with a friend's startup. He couldn't get response times within the time period needed for his third party integration. After some hacking, we opted to cripple his webserver. Turns out that you can slice out mass amounts of the http protocol (and in that time server overhead) and still meett all of your needs. Sure it needs a recompile - but it worked and scaled, far more then anything else they did. Their exit proved that point.
Nice statement.
I think there is another equally pervasive problem: balancing between shipping something and strategizing a complete "operating system" but in the eyes of OTHER stakeholders.
I'm in this muck now. Working with an insurance co that's building internal tools. On one had we have a COO that wants an operating model for everything and what feels like strategy/process diagrams as proof of work.
Meanwhile I am encouraging not overplanning and instead building stuff, shipping, seeing what works, iterating, etc.
But that latter version causes anxiety as people "don't know what you're doing" when, in fact, you're doing plenty but it's just not the slide-deck-material things and instead the tangible work.
There is a communication component too, of course. Almost an entirely separate discipline.
I've never arrived at acceptable comfort on either side of this debate but lean towards "perfect is the enemy of good enough"
The most important aspect of software design, at least with respect to software that you intend not to completely throw away and will be used by at least one other person, is that it is easy to change, and remains easy to change.
Whether it works properly or not, whether it's ugly and hacky or not, or whether it's slow... none of that matters. If it's easy to change you can fix it later.
Put a well thought out but minimal API around your code. Make it a magic black box. Maintain that API forever. Test only the APIs you ship.