Prioritization for Perfectionists, or: How I Learned to Stop Worrying and Love the Non-Perfection

Are you a perfectionist? That kind of a person that can never say “I’m done”, “it’s ready” or “let’s ship it”? The one who can’t release the new feature unless it’s polished and perfect?

Me too. I just don’t seem to be able to stop. When I work on my personal projects, the backlog seems to grow every minute.

"Refactor this!"

"Improve that!"

"Make that button align with other buttons on another screen!"

"Improve drawing speed by refactoring X!"

"Write more comments to that algorithm!"

"Write tests for Y and Z cases!"

"Optimize this algorithm!"

And even:

"Redraw this icon", "change the color of this button", and "move this one pixel to the left".

I can’t even publish an article in my blog without re-reading it many times and rewriting a couple.

I can’t even tweet without deleting the tweet and reposting it.

Guess how many times I changed the following header, just swapping the words in it there and back? 🙂

Ice and Fire

Yes, this header.

Pretty chill

My career started at a company where we paid much attention to code quality. This played along very well with my perfectionist nature. I could polish a four hour task for a couple of hours more than needed, before giving it out to code review. I was always in the mood of adding just a couple more tests to a complex business logic module.

The company didn’t have plenty of developers nor lots of money to hire more, but our users were demanding, had a lot of complex business requirements, and their work depended on our product and its quality very much. So the code quality was very important. Because of that, the bugs were fixed on time and completely, and the developers didn’t spend lots of time adding new features, as the system grew.

And it worked: by investing some more time in the beginning, by being slower, we managed to save more time later. How many times have I looked at a new ticket and thought: now nice we’ve refactored that place a month ago! Now it’s much easier to implement this new task.

Then I changed the job, and I found myself in a completely opposite company.

Hot lava

In this company the speed of delivery was valued more than anything. It was okay to ship completely messed-up code, but you should’ve done it on time. And no one is allowed to refactor. There’s no time for that. Ship features, and ship fast!

This approach has its points. If you are not on time, you’ve failed, and never mind how good your code was. Also never mind how bad your code was, if it was on time and (at least partially) worked. The business was above all, the time was money, the product was important, the code was secondary and disposable.

No one cared too much about bugs too. Well, sometimes it fails, sometimes it displays invalid data, but there’s no time to fix small things. No tests too. New features are more important.

But how many times have I looked at a finished task and thought: it took me just half of the time it’d take if I was doing it as comprehensively as before. Sometimes it felt like slacking, I must admit, but it was much faster and more business-effective than in the previous company.

This approach was very new for me, and it took me some time to digest it, but I’ve found the middle ground at last.

My middle ground

In the winter, soon after I started at the second company, I also started a pet project – a small Android app called Word Cloud which, well, makes nice and colorful word clouds.

I can visibly compare how different my approach was back then, when I just started. I took time to refactor the code just because I didn’t like it. I spent time fretting over perfect button sizes and best icons for them. I wanted to apply all the best coding practices there were.

The active work took a month, maybe two, of fairly relaxed coding in evenings and weekends. By the beginning of spring I had a finished project in my hands, ready to be used.

And still, it took me another half a year to ship it.

And I wasn’t actively adding new features in that half-year.

I had an internal block in my head, and I just couldn’t publish it. I was very afraid that the users will see how my buttons and their icons are not perfect. I felt uncomfortable that I had a circular dependency in my code. I was embarrassed that there’s a class in my code that’s quite big, and I should have split it in smaller pieces. I was ashamed of another singleton class. I was disgusted by the fact I’m reading the settings file more often than absolutely necessary. I was depressed that I had that five-line piece of code copied and pasted with just one small change. I had a class that was named badly.

Can you imagine? I couldn’t publish the finished product until I fixed the bloody singleton and the blasted circular dependency and the abominable naming.

So for half a year I was basically procrastinating. I was refactoring small things, adding comments, renaming things, but wasn’t making any big changes or adding features. I was purely making my code better before publishing the app.

Then, one nice evening in October, I pulled myself together and pushed the button. (Pulled and pushed. What a pun!)
The app was live.
And nothing bad happened. Users were downloading the app, they were saying nice things about it, asking for more features, and overall it went well.

This blew my mind. The users didn’t notice my singleton and my massive badly named class! They genuinely didn’t care about me having an if instead of a switch. They paid no attention to the lack of comments in a file. Users didn’t care about my code.

The second release took much less time. The third one, even less. I deliberately decided NOT to refactor some things that were bothering me. I firmly limited myself when I’m too involved with petty fixing and improving. I learned when to say to myself: “Stop, it’s done, let’s ship it” and “It’s good already, don’t worry too much”.

It was like teaching myself to walk, but instead I was teaching myself to stop. The first step (or stop, in my case) is hard, the second is easier, and so forth.

Since then, my releases pretty much don’t depend on my perfectionism, but on readiness of features. By “ready” I don’t mean “perfect”, I mean “good enough, maybe better than average, but not perfect, if it takes too much time”.

I still can dive deep into a refactoring, but now it must be justified by a feature coming up, or by a bug I’m trying to fix. No more “just because I don’t like it”. No more preventive refactoring.

What about the code quality?

My code didn’t become worse because of it.

Years of working in the company that values the code quality taught me how to write readable code from the very beginning, how to separate it in blocks with clearly defined responsibility, and where to put comments to make it even more clear.

But years of working in the company that values speed and the product taught me to value the product too.

I learned to make right trade-offs.

My code is not spotlessly perfect. But it is good enough to become a product.

Instead of the morale

Your users don’t know about all your struggles. The product isn’t going to stop being interesting for them because there’s a method in your code that accepts eleven arguments. Yes, it would be nice to make them into a configurable object, but you don’t have time right now, and it shouldn’t stop you from shipping.

The users are not going to laugh at you because you have that dreaded singleton helper and don’t have the courage yet to make an object and pass it through everywhere you need it. Yes, you’ll do it one day, but you shouldn’t wait until you finally make it.

You shouldn’t wait until you finally fix everything in the world, and your code is shining like a diamond. You should ship. The users care about the product, not the little imperfections.