Simplicity in Software Design
Not many people would argue that a complex and difficult-to-understand solution is better than a simple and easy-to-understand one. And still, we often tend to end up with more complex solutions than we need and/or would wish for. This is generally for two reasons. Either we think to little, or too much.
Thinking to little
To some degree, programming is just mechanical work. We know what we want the computer to do, and we just need to write down the instructions. However, although we normally know what we want the computer to do, we don’t necessarily know the best way to do it. Figuring out a good way to do this is what programming is all about.
Unfortunately, the simplest solution is not necessarily the first one that comes into our minds. In fact, there seems to be an inverse proportionality between the complexity of a solutions and the time it took to reach it. Thus, simple solutions tend to take longer time and require more thought to find.
That makes it all seem very simple, right? The more you think, the better chance you have of reaching a good solution. Perfect! Then you could stop reading right now, and just spend some more time thinking. Right?
Thinking too much
Well, not quite. This is the time where programmers tend to get carried away. They get too eager, too ambitious. Programmers like to write complex code. They want to create the Perfect System™, reach design nirvana. There is something very satisfying in designing those perfect systems; extremely flexible with intricate interaction, beautiful class hierarchies, perfect cohesion and so little coupling that you have to use a magnifying glass to see it.
Not only that, W.E. Boebert, designer of an autopilot that ran for 15+ years without an in-flight anomaly, suggested that education also is a factor in programmers desire for design complexity.
I laid full blame […] on CS faculty who either knew nothing other than operating systems or held OS designs up as the ultimate paradigm of software. So CS students and new grad software engineers came out thinking that an autopilot should look like Unix.
So when you recognize those feelings inside you, it is time to stop and take a deep breath.
Doing the right thing
Most likely what we need right now is not the Perfect System™. Most likely we need a simpler, less complex system. While this might sound boring, it is pretty much almost the truth. I’m sorry. For some inspiration, read those words of wisdom from Queen Christina of Sweden, freely translated by me:
Greatness lies not in doing what one wants, but wanting what one should.
But come on you might say, why not build the perfect system? After all, if we do build a perfect system, it’s not that anyone is going to complain, right? Well, no. Chances are however that you’re not going to reach the perfect system. It is much more likely that we end up with a system which is not quite what you intended, overly complex with a class hierarchy with more levels than a Nintendo game. And while it can handle any future change you could ever anticipate, still doesn’t do exactly that we need right now. Without doubt you have spent way more time than can be justified and it still doesn’t work. Of cause, there are exceptions, and I might be exaggerating just a bit, but you get the general idea.
Extreme Programming stresses among other good habits the importance of, and time gain one gets from, not writing an overly complex solution in their first design rule:
Always do the simplest thing that could possibly work.
Benefits of simplicity
The saying goes that “less is more”, and this is very true when it comes to software design. By keeping our system as simple as we can we gain one very important quality; it makes our system easier to understand. Kernighan and Plauger wrote in 1978 as the first rule in their classic programming book Elements of Programming Style something which still, thirty years later is as true as ever.
Write clearly – don’t be too clever.
Actually, as long as it is syntactically correct, a computer has no problem figuring out what you mean no matter how the code is structured. The same is certainly not true for humans, however. Therefore, don’t write code for computers, they don’t care how it looks anyway. Write code for yourself and for your fellow co-developers – they certainly do care. And believe it or not, most of us actually have a limitation for how many layers of abstraction and indirection we can handle before exploding.
Another good reason to avoid complex solutions is that it generally is harder to debug programs than to write them. So, put somewhat jokingly, if you are as smart as you ever can when you write the code, how can you have any hope of ever being able to debug it?
I’ll sum up with the words of C. A. R. Hoarse, brittish computer scientist, perhaps most famous for developing the quicksort algorithm.
There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult.
Which way are you constructing your designs?
I have to say that this text is very nice. I particularly share most of the opinions in favor of simplicity you’ve described here.
The only one thing that I would like to add, as a comment, to your text is the following idea, or suggestion, (or mantra, if you will
).
Everytime one feels tempted to extend that simple required feature that she is implementing, this person should ask herself a question:
“what is the trade-off between spending time on implementing a ultra general design and successfully implementing the required feature with a simple, yet less general, design?”
In other words, do those hypothetical extensions that would justify the general design really matter now?
Yet another question, does a design need to be ultra general to be a very good design? I do not think so.
Eli - May 2nd, 2006 at 23:14Thank you for your comments!
Indeed, that is a very justified question to ask. Also very much connected to XP’s rule “Always do the simplest thing that could possibly work.”
And for your last question, for most cases I would say no. In some special cases it might be valuable (e.g. when desiging something like the .NET Framework). But in a general I think ultra general designs tend to be harder to understand.
Henrik Jernevad - May 3rd, 2006 at 02:14