Iteration: The Antithesis of Perfection

Why it should be quantity over quality

Baljinder Benipal
3 min readMar 15, 2021

TL;DR

Writing a lot of great code is better than writing a little bit of perfect code.

Perfection

When an interviewer asks, “What is your biggest weakness?”, a staple reply is, “I’m a perfectionist”.

On the surface, this is an excellent response because perfection is not undesirable in and of itself. No one is going to complain when they’re given the perfect solution to a problem. It becomes an issue when perfection manifests itself in missed deadlines and premature optimization.

In reality, perfection is the scourge of development. No one really knows what it looks like, and the uncertainty surrounding it is a perilous trap.

Missed Deadlines

The trouble with a perfect solution is that it takes time; however, in the fast-paced world of software development that’s a luxury that isn’t always available. Perfection is easy to sell on paper: imagine writing software that is provably bug free and readable. It sounds too good to be true because it usually is.

There’s a reason that formal verification isn’t used outside of certain mission critical software. To afford the confidence of perfect functionality takes a great deal of effort; arguably more so than writing the original software. Plus, with regard to code style, perfection simply does not exist. Personal preferences mean that someone will always have something to nitpick about the formatting of the code, variable names, etc.

Perfection is the enemy because striving towards it is a time intensive process that is practically guaranteed to make any deadlines impossible to meet.

Note: This does not mean that it’s acceptable to be actively negligent for the sake of speed either. Consistent coding styles and practices go a long way for readability. Also, technical debt exists and rushing to a solution is no better than endlessly refining one.

Premature Optimization

Premature optimization is the root of all evil
- Donald Knuth

The perfect software is built to handle every edge case possible. It’s hard enough finding all of these cases before releasing a product, but solving them is its own mess.

Writing robust code is a noble endeavor, but if it means sacrificing readability or taking a massive hit to performance, one should tread carefully. Consider the following code (in JavaScript):

function getReversedNum1(num) {
let result = 0;

while (Math.trunc(num) > 0) {
result *= 10;
result += Math.trunc(num % 10);
num /= 10;
}

return result;
}
function getReversedNum2(num) {
if (!Number.isInteger(num)) {
return NaN;
}
let result = 0; while (Math.trunc(num) !== 0) {
result *= 10;
result += Math.abs(Math.trunc(num % 10));
num /= 10;
}

result *= (num < 0) ? -1 : 1;
return result;
}

Both of these functions return a number consisting of the same digits as the input, but in reversed order. The difference is that getReversedNum2 explicitly defines behavior for negative integers and non-integer values. The complexity of the second function is not overwhelming, but it’s definitely a bit harder to read and requires some extra function calls. Being more robust can be useful, but suppose we knew the input could only ever be positive integers. Then, it’s no longer necessary to opt-in for a more comprehensive solution.

Iteration

An effective way to overcome the lure of perfection is with iteration. Rather than painstakingly writing a solution that can do every thing from the get go, it’s more practical to start small. A minimum viable product is better than an ambitious solution that never makes it out of the pipeline.

In particular, iteration is useful for focusing specifically on bottlenecks. For instance, a programmer can set up caches before even writing code to fetch resources, but that’s usually overkill. That time is better spent building out other parts of the product. However, if there is high latency in server response times because of too many API calls, then it might be time to add caching layers.

Iteration can also be useful when dealing with efficiency. Personally, I used to desperately chase performance gains with mixed results. I still advocate for performant code, but now I focus a lot more on the big O notation. If the performance gain has a negligible impact and requires an unjustifiable amount of work or makes the code much less readable, I probably won’t go through with it.

All in all, skip the temptation of perfection. There’s a lot of cool stuff to do, so don’t dwell on the minutiae!

--

--