After 15 years of writing software, I’ve accumulated several thoughts on software design.
I didn’t go to school primarily for Software engineering, and I haven’t spent as much time reading heavy books on the subject as I would like. Most of my career comes from hands-on experience, and learning from both successes and past reflection. Indeed the phrase Design Pattern was a foreign one to me until a decade ago when I started realising some of what I had been practicising had terms coined for them.
Through experience, I’ve gravitated to several favourite patterns and key concepts. On a new project these are the MVC (Model-View-Controller) pattern, the DRY (don’t repeat yourself) principle, the use of Convention over Configuration, the use of reflection, protocols, state engines, recursion, and remote configuration.
Sounds a little like the kitchen sink – doesn’t it?
But sophisticated maintainable software almost completely relies on design. While there are many facets to design, one that I focus on heavily on is how reusable code is.
I hesitate to say this can be achieved by reading design pattern books, or the use of technical skills, as several times I have made the mistake of repeating these errors.
All too often, we developers find ourselves knee deep in projects that have been underestimated and overcommitted. In this new state of reality, where the demands of the customer are louder than everything else, the first impulse is to code and forget the principles behind what makes good software design. So suddently you catch yourself adding an IF statement in code which previously was pristine and had only a single branch of flow. But this should be your first clue that perhaps it’s time to think about refactoring the design into a higher level concept.
By higher level, I mean the use of Superclasses or Protocols. There should never be special-case exceptions in software code, and common practices such as using IF statements to determine the logic to proceed, are really just hacks, tricks, and frankly not a shortcut, or temporary. These quick solutions usually get baked in.
But what if it’s just for a slight exception to the code to get to code-completion; and well this project isn’t probably going to be touched again. Isn’t that ok? Well the answer to that is subjective, and depends very much on the task at hand.
The truth is, it’s very rare that any piece of software is perfect, and it’s even rarer that a project ever ends. It may have stages where development is idle – even for several years – but software that is used on an ongoing basis, will eventually have requirements for extension. And thats when our special exceptions can cause havoc.
So its in those hours of greatest need, those where the project is due and the customer is saying we need it now, that we need to tread carefully with the code we write. And thats where self-discipline comes into play.
Discipline begins to waver on long projects when developers are exhausted and there seems to be no light at the end of the tunnel. It’s these moments, where the sum of exhaustion, frustration, pressure, morale starts to cloud judgement – and instead of good design, we practice rapid coding. And rapid code, rarely is good code.
So be aware of your own level of frustration and exhaustion on a project, and act accordingly. Sometimes a project is malleable and things can be left out. Other times, well it depends on the situation; and there is no way out but to code rapidly.
IF I might need this…
Another common practice I have encountered repeatedly over the years, is one I thankfully recognise and do not practice myself. This may be because I like to build my software as lean as possible; and I tend to refactor and abstract code when the design warrants it.
A number of projects that I have joined, have gone the opposite direction. The lead developer has read a dozen books on the technology being used; and on the onset of a project have decided to add what they think are good design principles.
If I might need to localise this application, I should write code to support that right now. If the application may be too slow, I should optimise this section right now.
Yet another IF statement.
There’s a fine line between not enough design, and overdesign. As a developer I need to be constantly walking this line.
Good software design is about how much is put into, as how much is left out, and to paraphrase a very well known quote: “premature optimization is the root of all evil.”
Premature design is also a source of evil. It can complicate, obfuscate, and even hinder the design of code before it has been written. This is because so much effort is being put into following unneeded design elements that might or might not be useful.
Many times the design of an application is not apparent on the onset of coding. By leaving out the nice-to-haves, code is leaner, less noisy, and much easier to refactor.
So be aware that the design principles we put into our software, should first and foremost be the ones that accomplish building the application so that it achieves its major functional needs. The nice-to-haves can be in many cases be added after.