Shipped is better than Perfect

A practical warning about pre-optimization and my mantra for escaping it.

Featured on Hashnode

I've been working on a little webapp called Tweet Sweep . It was supposed to ship March 1 as my February project. Then it was my March 15 project. Now it's just my "March sometime" project. I'm stepping back from it this morning to do a post-mortem on that slip and ask how this happened.

The answer is simple: I fell into one of the oldest traps there are for creators. If you've made things that you intend to share with others at some point I am sure you've been here, too.

This trap comes by many names: "don't let the perfect be the enemy of the good", "shipped is better than perfect", etc. But, no matter what you call it it looks the same: you find yourself building to defeat every future critic and challenge you imagine your project will ever have to face. This is the same whether you're an artist doing "one more tweak" to your writing or your photographs, or whether your a maker building out product features for an audience you haven't yet met.

Preoptimizing Products

For those of us building internet products, this usually looks like building the version of your project that you imagine will be necessary assuming it will be a success. Once you imagine a challenge your app might face, you immediately forget everything you've read about "Minimum Viable Product." Now instead of building the shoddy skateboard prototype you need to validate your idea, you are building a tank that can withstand whatever fears have snuck into your head - whether it's picky enterprise customers or a millions-strong horde of Redditors.

For me with Tweet Sweep, this took the form of going far too deep on my differentiating feature. There are plenty of great apps today that will delete all your Tweets, but I'm a sentimentalist so I wanted to make a simple webapp that would let me set up filters around what to delete and what to keep. This is a strong start - I have a use case that's already been proven and a key differentiator to set me apart from what's in the market.

However, the trap was set as I started thinking about how to build those filters. I fell down a rabbit hole constructing a deep, extensible, serializable filter framework. I had imagined a monstrous, mythical enterprise customer who would want to use my free app to build expansive AND/OR/NOT logical trees to determine which Tweets they kept and which they deleted, and now I was building for this Minotaur rather than the normal people like me. After a week, I had 30 files and a thousand lines of code that laid out a feature no one, not even I, had asked for.

Minotaur Attack

How do we escape?

So you have found yourself pre-optimizing. Or, worse, you haven't found yourself pre-optimizing and you are still blithely building out features no one needs so you don't have to do the scary thing and ship. How do we escape this trap?

This is where I use a mantra I picked up at Foursquare: that will be a nice problem to have.

Do you feel the urge to build out infrastructure on your 1-user product so that it can withstand millions of users? Take a breath. Recognize this is pre-optimizing. Tell yourself that will be a nice problem to have. If your product is so successful that millions of users join so quickly that you can't shore up the infrastructure, you have a billion-dollar success on your hands. Hire a team and buy some champagne.

Remember that Twitter had years where the site would "fail-whale" for hours on end. But that didn't stop them from becoming a $54B company today. What would have stopped that success is if the founders had delayed launching for years until they had built out the infrastructure to withstand today's usage. If they had then we might (gasp) all be using Google Buzz instead.

And so for me, I looked at the work I was doing to build out an unparalleled amount of flexibility in a user's ability to filter precisely whether to keep or delete a single Tweet and I thought, "if a user wants this, it will be a nice problem to have. It will mean that someone cares so much about this problem, they value that level of control so highly, that I can probably sell that feature to them. But, I have to finish this before they can tell me that."

So I scrapped the 30 files of complex logic, serializers, deserializers, etc. I sat down for one afternoon and I rewrote a scrappy version that uses a single JSON object to encode basic rules in a way that I could understand, build, and debug quickly:

{
  name: "Filter old, unpopular tweets",
  joinMode: "all",
  age: {
    enable: true,
    comparison: "greaterThan",
    value: { value: 6, unit: "months" }
  },
  likes: {
    enable: true,
    comparison: "lessThan",
    value: 5
  }
}

With this one refactor, I unblocked myself. I made more progress in 6 hours than I had in the whole week prior.

That is the power of recognizing and escaping the pre-optimization trap. That is why so many maker advice columns exhort you to build the "minimum viable product." It is because you can do it fast and then you can ship. If you don't ship, you won't know whether your idea will attract 1 user or 10 million. That kind of multiplier doesn't come from the flexibility or future-proofing you put in before launching, it comes from whether your idea has found a market and a niche that wants it. No amount of pre-optimization will fix a product having no market, and almost no lack of optimization will sink a product that meets an unfulfilled need.


To hear about Tweet Sweep as it gets closer to launch this month, sign up here .

Syed Fazle Rahman's photo

I totally get where you are coming from Zack Sheppard. 🙌 Have learned this the hard way.

Ship fast, break things, and iterate.

Benoît Bouré's photo

Excellent article.

I am still trying to get out of the same trap (almost there too 😊) and learning from my mistakes for my next project.