Worse is Better Start Simple and Iterate
I hold myself to a standard of simplicity and clarity, and strive for perfection in my work. I write stuff clean (not clean code TM), I document my work, I write tests, and I follow good practices and patterns(not always to a T). But I never ship the original version of my work to perfection. Not from laziness but conviction. Perfectionism isn’t rigor, it’s cowardice. Cowardice that your code might be seen and dismissed. Good enough isn’t good enough, not really. You still hone your craft, still cut the wood true. But you don’t carve every possible notch for problems you haven’t seen before you raise the rafters. Ship what stands strong, iterate when the storms show where it leaks. Because trying to predict every crack before it appears is just paralysis with a blueprint.
Paralysis by Analysis
If you have even a bit of a perfectionist in you or a love for your craft, you’ll know the feeling of wanting to only show your best work and only on your own terms. You want to foresee every possible issue and solve it before it even appears. You spend more time thinking about the work than actually doing it. Obsessively implement features or edge cases that you may never need but want to have just in case, because they might be needed later. Set yourself in the mind of the end user and try to predict every possible way they might use or misuse your work. Here’s the thing: you can’t, you will never be able to predict every possible edge case, every possible use case, and every possible mindset of every possible user. You can try, but there lies madness. The more you try to predict, the more you will find yourself stuck in analysis paralysis. You will be building yourself into a corner of your own making, unable to move forward because you are so focused on making everything perfect that you can’t actually finish anything.
You Can’t Predict The Future
You have given yourself an impossible task. You wish to predict events before they happen, and what’s more, you wish to be given the magical power of being able to think about worst-case scenarios like thousands of different people all at once, and at best tens, hundreds of thousands, or even millions of different people all at once. Obviously, this is unreasonable, so why do you try to do it? You’re not gonna be able to predict the future, you won’t be able to foresee every possible issue or use case, at best, you might be able to predict maybe 1% and that’s generous!
Let’s take a simple problem, shall we, something like building a shopping list app. Simple, right? It’s basically just a glorified to-do list app with a single purpose, a to-do list that specializes! Obviously, you’re gonna need a list of items, the ability to add items, remove items, and most importantly, the ability to select items as bought. There we go, a perfect shopping list app. But wait, if the user ticks something as bought, it just disappears from the list. Wouldn’t it be better if we sorted it to the bottom of the list and applied some conditional styling to it to signal that the item has been bought and allow the user to untick it if they made a mistake? Oh, how about quantities? Surely, we should have a special case just for quantities, or perhaps sub-items. Why do we need expandable sublists now? Oh, now the list got cluttered. What if we add a clear all bought button and a general clear all button? Hmm, some users might wanna categorize their items into sections like produce, dairy, meats, and bakery. Oh, that reminds me, perhaps we should allow users to make special lists for different stores, after all, one store might have better apples and another might have a deal on milk. Oh, and what about sharing lists with family members or friends? We should totally add that, oh, whoops, now we need to handle conflicts when two people edit the same list at the same time. Oh, and what about offline support? We should totally add that too.
… See where this is going? I could keep this up almost forever, but you get the point. You can keep adding features and edge cases to your app and trying to cover every single use case and every single edge case, but what will you have at the end of that process? A bloated, overcomplicated app that tries to do everything but ends up doing nothing well. You will have spent so much time trying to make everything perfect that you will have never actually finished anything.
Start Simple and Iterate
This is not rocket science, I am not telling you anything new here, but always ship the simplest version of your work first, some call it a minimum viable product (MVP), others a minimum lovable product (MLP) or even a minimum marketable product (MMP). I don’t care what you name it; the point is to solve the problem you set out to solve in the simplest, purest way possible and then iterate on it after you get actual user feedback. Solve the problems of real users that are enthusiastic about your work, not the imaginary problems of hypothetical users that may or may not exist or never care about what you have built. This way, you will build a product together with your users via constant iteration and feedback loops. You will be able to focus on the problems that actually matter and not get bogged down in the weeds.
And if your idea sucks? Well, you’ll find out faster! Better to find out that the very core of your idea sucks before you spend months building something nobody wants or needs. Freeing up your time to move on to the next idea faster. And we all know, as developers, we have a whole graveyard of unfinished side projects and experiments.
Embrace Imperfection but Never Compromise Quality
In my previous blog post Settling for Boilerplate Over Simplicity, I briefly touched upon the idea that we should never accept “good enough” as good enough. We should be proud of our work and strive for quality in everything we do. As with all things in life, there is a fine balance to be struck here. Do not compromise on the quality and craftsmanship of your work, but do not go so far that perfectionism paralyzes you from actually ever showing your work to the world. Produce something you can be proud of, something that stands strong, and iterate on it. Embrace imperfection, accept that there will be imperfections in your work, and those imperfections will eventually need to be addressed. They could be anything from critical bugs to missing features or even just usability issues. But do not let the pursuit of perfectionism stop you from allowing your creations to see the light of day.
Putting This Idea Into Practice
So far, I have mostly talked about this idea in abstract terms, but how do you actually put this into practice? What are some concrete steps you can take to avoid paralysis and allow your work to see the light of day? The first and most important thing for me is simply embracing criticism as not a slight against you or your work but as an opportunity to learn and grow, if a user has a visceral negative reaction to your work, that at the very least means they cared enough to try it, even if they are rude or obnoxious attempt to extract useful feedback from their criticism. The second step is to set clear goals on what you actually want to solve and what a minimal solution to a problem you wanna tackle looks like, and stick to it. This one is incredibly hard, as we tend to get very enthusiastic about our work and want to add all the bells and whistles right away, and not do the boring work first. But resist the urge, build the core first, and then iterate after you have something concrete. The third step is sadly also the hardest: get your work out there, share it with the world, put it on GitHub, deploy it to a server, publish it on the web, write about it, tweet it, share it with friends, do whatever it takes to get your work in front of real people. I find this step insanely hard because, in essence, it’s marketing and feels a lot like metric-seeking behavior. But it is a necessary evil, just avoid metric seeking for the sake of metric seeking. Your goal is to produce something real people will use and care about, not to chase vanity metrics. Finally, once you have your work out there, listen to the feedback, iterate on it, improve it, fix bugs, and add features that users actually want and need. Rinse and repeat.
Build Something Cool
The idea of worse is better is not about cutting corners of producing subpar work; it’s about embracing imperfection on the journey to perfection, it’s about doing more and producing more, it’s about releasing your work to the world and iterating on it. It’s essentially about open source development, both of your skills and of your work. So build something cool, solve some problem you have, and let the world see it.