Writing perfect code: a story on paralysis analysis

Photo by Roman Kraft on Unsplash

In my early days as a software developer, I was only concerned with the results of the code I wrote. I was creating things — it didn't matter how. But one day I found myself asking, “Should I return null or an empty collection from this method?” After giving it some thought, I considered the empty collection to be better and quickly found confirmation on the internet.

I started thinking about the code itself, not only its outcome. Code that I previously wrote suddenly felt ignorant. I said to myself, “From now on, I will always write my code this way.” I felt like a more competent developer and began to put more thought into the code I wrote.

After I while I found myself getting stuck, thinking too much about every little detail. I was suffering from analysis paralysis. With every line of code I wanted to write, the idea of something not being perfect about it kept me from writing it. My productivity plummeted. I wanted to write perfect code all the time. The only way to achieve that was by not writing code.

Perfect code is somewhat of a paradox. As you become more experienced as a developer, you start to see more and more ways to improve your code. Writing perfect code doesn't become easier, it actually becomes harder as you become more skilled. Ignorance is bliss.

How to overcome analysis paralysis?

Coding used to be something I enjoyed. But that started to fade because I was overthinking every line I wrote. Instead of creating something, I was only doing research into best practices. I learned a lot in that period, so it's not all bad. But I realized my way of thinking had to change.

One thing that helped — and still helps to this day — to overcome analysis paralysis is unit testing. Rather than trying to design and validate everything in my head, unit tests force me to write it down. More importantly, the validation of the design is partly handled by the compiler and unit tests, resulting in less mental strain. Test-driven development is especially useful if the inputs and outputs are already known.

Another thing I learned is to embrace code as a living, evolving thing. My education mainly focussed on upfront design, treating code as the final, static product of an elaborate design phase. In reality though, I found that code is always changing, even more so in agile software development. Instead of seeing it as a deliverable, I started to appreciate code as a versatile tool. I used to fear writing a line of code, because I was worried it wasn't good enough for the final version. Now I love writing new code, knowing that it will change and evolve into a better state — far better than any upfront design can achieve.

Code as a sketching tool

A painter starts with a sketch. Those sketch lines won't be visible in the final work, but they are crucial in supporting the painting process. Programming isn't very different. The code you write may not stay around for very long, but it will help shaping the rest of the system. That is where the value is. The effort spent on a piece of code should contribute to its usefulness as a dynamic tool, not to its perfection as a static deliverable.

Programming is not an exact science. It's not about detailed upfront designs that are then built in a single iteration. Coding is an art; it's about exploring, sketching and revising.