When asked to describe the architecture of a software system, the first thing developers usually do is picking a graphical tool to begin with. Maybe Visio, Xfig, OmniGraffle, or some other fancy UML tool. Maybe even Knuth’s MetaPost. These tools are all chock full of drawing features, and they keep you all too busy playing with the looks of a system’s design.
Aren’t we primarily interested in describing and thereby explaining the design of a system? More strongly, isn’t the actual goal communicating a design? For instance, in order for a development team to agree on a design, commonly a design is documented in a design specification: the document communicates the design to all team members. Personally, I am regularly communicating (new) design idea’s to my development team too. For that purpose, I’d generally use a whiteboard or a couple of sheets of paper, before the design ends up in a full-blown document. Even while I am in the process of writing a large (undocumented) chunk of code on my own, I typically take some paper lying around to outline the design I’m working on.
So before grabbing another fancy tool, I want to try to explore what I’d actually need while I am describing an architecture. Not only when I am using a (graphical) software tool, but also while I am drawing on a whiteboard or a piece of paper, talking to colleagues, and so on.
Remains the question whether there are basic elements — or principles — for software design to be defined. I believe there are. That’s what this article is about. I even want to go as far as to say that I’m able to define my personal five most important principles for software design. The ones I use, I would say almost daily. Not exaggerating, let’s say I use them a lot, taking into account those days I’m having a Hoegaarden in the April sun at Queen’s day .
Back to software design.
The question was: “Are there basic principles for software design?” My answer is: “Yes, and I’ve got five here to present to you.”
Let me start at the beginning. First of all, to get grip of a system, I would want to divide it into parts. Starting with big chunks. Dividing those into smaller chunks. Going on until I decide it is time to stop. The whole system itself is a part too; the largest one. These chunks, or parts of the system are usually called entities, the system itself being the largest entity. Voila, here we have my first principle of software design: entities.
Entities divide a system into parts, the parts into sub-parts, these sub-parts again into sub-sub-parts, and so on. To structure this ever growing set of parts, I introduce my second principle of software design: hierarchy. The whole system itself is the entity at the top of the hierarchy. From there we can zoom in to the system’s different parts, representing the next level of hierarchy. These parts again can have subparts defined in another level deeper in hierarchy. And so on. The bottom level of hierarchy has the highest level of detail. It depends on the level of detail you want, how much levels of hierarchy you will have.
Using entities and hierarchy, a system can be divided into parts with the parts being divided into different levels of hierarchy, describing a system’s modular structure. But that does not tell us anything about the interrelations between entities, besides that they are ordered into different hierarchical levels. My third and fourth principles of software design, inheritance relations and instance relations, are the concepts to describe relations between entities. Modern programming languages actually have these two very important concepts for re-use — specialization and instantiation — usually built in.
An inheritance relation — or specialization — in terms of entities, is the concept that one entity inherits from, or specializes another entity. I believe this is such an important and truly valuable concept, that you should be able to reason about it on the level of a system’s design. To comprehend an existing system, you will really want to understand its inheritance relations.
Instance relations are the relation between an entity with one or more other (instances of) entities. We all know this elementary form of re-use (commonly termed composition) and put it to use everyday. Except on those sunny Hoegaarden days of course. Undeniably, instantiation relations determine a large part of a system’s structure.
To conclude, on the same level in the hierarchy I will always want to describe the inheritance and instance relations, as they are two important concepts that determine the structure of a system for a great deal.
Now, at first, this might be trivial, but besides the first four principles, the remaining one is: explanation. “Hmm,” you’ll say, “you are leaving out all the other fancy schmancy stuff, and put it all under the name of explanation?!” I’ll answer: “Yes. That is precisely what I am doing. I am trying to keep it simple.”
To explain why, let’s shortly get back to the first four principles. They all describe static properties. They define what I’d rather call the blueprint of a system: its static structure. Shouldn’t a design contain the dynamics of a system too? In other words, a design should tell you, on top of the static structure, how the software will behave when it is in operation:
design = blueprint + behavior = static + dynamic structure
“Aaah!” You should say now. “That’s what you mean by explanation.” Yes. That is what I mean with explanation. I know of no model that can really capture dynamic elements. But words can! Maybe you need more than a couple of them, or a whole paragraph. Maybe even a few paragraphs. In all cases, those words can bring your static structure description to life.
Yes, there are modeling languages to capture the flow of a system. However, I generally think trying to model a flow in a diagram, still makes it static in the end. I have seen diagrams including attempts to depict the dynamics of a system, that obfuscated instead of clarified a design description. You just forcibly try to capture dynamics in a static graphic. Even if you do try to add dynamics in a picture, most of the time you will still need words to explain the diagram.
So why not just use words?
A picture might say more than a thousand words. On the other hand, it might also be impossible to capture a well written piece of text in a single graphic. It simply depends on what you are trying to communicate. And in our case, this means that the static structure description can be captured neatly in diagrams. Little clarifying text will be necessary. The dynamics of a system, however, I believe, can be explained best with text.
Summarizing, the first four principles I introduced can be used to describe the static structure. Then the fifth comes in and brings the design to life by explaining the dynamic behavior, and, of course, an explanation for all other aspects of the design not captured by the static structure. Here they are, all five:
- Inheritance relations
- Instance relations
When I’m trying to discover, describe, communicate, or daydream about a software system’s architecture, I’ll generally stick to my own five principles of system design, as pillars for my mind.
|||I started writing this article on the 30th of April already, but lost an almost finished version of the text a couple of days ago. The only thing that remained was “The Five Princ”, a part of the original title “The Five Principles for Software Design”. My gut feeling tells me this has something to do with upgrading, or otherwise fiddling with Wordpress, grrrrr! Evidently, it took me a while reconstructing the text to an acceptable version again. I decided to keep the title inspired on the event.|