Our best work isn’t rushed. Excellence is not achieved through haste; creating something lasting, elegant, and reliable requires a sustained and focused effort.
I often feel as though, despite all of our protestations to the contrary, we have lost sight of that. When we build systems - and in particular when we build software systems - we often opt for the most expedient route even though we know that a more measured approach often gives better results and saves time over the long run. For some teams, this is brought on by the continual struggle for existence that they face: “We have to ship features in order to survive. Nobody knows if we’ll be here in 6 months, so let’s just get this out the door now with however much duct tape is required and live to fight another day.”
But then a year later, when we’ve paid the interest on that debt in hundreds of hours in lost programmer time, on systems we could have built better had we taken the time to do so, we don’t look back and say “I suppose we should do it right this time.” The pressure hasn’t let up, and who knows if it ever will.
Unfortunately, this has a strongly negative effect on the quality of what we build. The best systems work without us having to think about them. They’re the ones which were considered carefully, where the fit between the problem and the solution is tight and there are few gaps left for rot to set in.
It’s tempting to throw together a conglomerate of half-baked solutions and toss such prototypes into the world to fend for themselves, patching them up until they can’t stand up on their own before throwing the whole thing out and beginning anew. “Move Fast and Break Things” gains romantic appeal from its rebelliousness, but we move faster when things work. What we should rather be thinking is “Don’t be afraid to make mistakes… but when you discover you have, learn from and fix them.” Prototypes are invaluable for discovering the real shape of the problem you’re trying to solve, but they often fall far short of a fully realized solution to the problem. We must resist the temptation to accept a prototype as “good enough” and stop just as we start to understand what it is that we should actually be building.
I don’t mean to argue for any sort of (gasp!) Waterfall-style design process, nor do I think we should front load all of our attention at the onset of a project. In fact, I think that it’s almost impossible to know ahead of time what the real problems you’ll face are and to design around them unless you’ve already built the system once - hence the value of prototyping. Iterative improvement is a superior strategy, when executed well. Being stingy with your iteration time, and in particular sacrificing design time at the altar of speed, is a common cause of poor results. We need to be willing to give ourselves the space to reach the real solution instead of throwing half-baked facsimiles of them into the real world. When we lose our ability to do deep thinking and to truly engage in design, we lose.
Christopher Alexander understood this - he wrote a whole book on it titled Notes on the Synthesis of Form. Unfortunately, there are relatively few of us in the software world who venture out into the wider community of designers (I use that term loosely, to mean those who design anything - architects, engineers, cabinet-makers and the like) to see what we could learn there - though more of this knowledge is being incorporated as the industry matures. Alexander is quite popular in the Clojure community in particular. ↩
“A day of coding can save you an hour of design.” ↩