It was Voltaire who said, “Don’t let the perfect be the enemy of the good.” By that what I believe he meant was that while it might be good to strive for perfection we have to realize that we won’t always achieve it and if we are going to insist on everything being perfect we might end up not getting anything done. In the fast-paced world of business today good means good enough.
Any investment follows the laws of diminishing returns. We want our features to be good enough but not too good because if they’re too good it means that we’ve essentially wasted some of our customer’s money overbuilding some features.
Some of this is unavoidable because we can’t predict the future but I find that having good acceptance tests and a clear definition of our acceptance criteria can help us get really clear on how much of a feature to build before we consider it done. This may not seem like a big deal but to software developers, this is really big.
Nearly half of all features built across the software industry are never, ever, ever used. This means that there is a lot of waste in what we build so knowing what really is needed versus what we think might be needed is important. Just think of how much more effective you would be with half of your time back.
One of the key differences that I noticed between junior developers and seasoned senior developers is their notions around a perfect design and implementation of their designs. What I find is that the more senior a developer becomes less attached they are to making a perfect piece of code because we recognize that there are always trade-offs and our goal is not to write perfect code but to write code that is of sufficient quality that it gets the job done and is straightforward to extend in the future. Just doing that will get you ahead of 90% of the rest of the industry.
I used to believe that there was no such thing as a “perfect design” and so I shouldn’t strive for creating one. Instead, today I believe there is actually such a thing as a perfect design for a particular situation but the problem is it’s very costly to implement and so I find ways of mitigating that cost by making it cheap to go to a perfect design later in areas when and if it is needed.
The needs of software often change and as they do the requirements for the design changes but far too often we saddled ourselves with an existing design even though new requirements have significantly changed the problem and therefore we should be open to changing the design.
The thing that really helped me let go of perfectionism was reading Martin Fowler’s book, Refactoring: Changing the Design of Existing Software. When I realized that the process of going from a poor design to a better design is often safe, straightforward, and repeatable then I realized that it doesn’t matter where I start with the design, what really matters is where I end up!
One of my very favorite quotes from one of my favorite authors, Stephen King, is “murder your darlings.” Well, he is a bit of a violent and macabre sort, isn’t he? But his sentiment is quite valuable, I think. We tend to get attached to a design or a way of saying something or presenting something that may makes sense initially but given other factors and as we learn more, we may want to supersede the initial thought. I try not to get attached to anything but especially my own ideas and my own preconceived ideas of how things should be. This is one of the many things that magic, self-improvement, and software development have in common—focus on outcomes and let the details take care of themselves.
As software developers, our goal is to provide quality software that does what the customer wants at the most affordable price and we do this by building just enough of a feature in order for customers to derive value. This is why I believe that perfectionism and professionalism can sometimes be at odds with each other. How can this be resolved? Well, stay tuned because I’ll discuss that in my next blog post.
Note: This blog post is based on a section in my book, Beyond Legacy Code: Nine Practices to Extend the Life (and Value) of Your Software
Previous Post: « Share Common Quality Practices
Next Post: Understand Trade-Offs »