Software Design Introduction
While most of us have an idea about what the term software design refers to, the concept of software design is not actually that clearly defined. In this software design tutorial I will attempt to capture the various different meanings associated with the term software design in a single coherent model that is easier to understand and discuss.
If you prefer video, I have a video version of this tutorial here: Software Design Introduction
What is Software Design?
When people talk about software design they typically refer to one or more of the following phenomena:
- The process via which software is designed (AKA "Design").
- Figuring out what functionality a software system should have.
- Figuring out how to structure the software that implements the functionality.
- The result of the design process (AKA "The Design").
- The decided functionality itself.
- The decided structure of the implementation.
I have incorporated these software design related phenomena in the following conceptual model:
As you can see, the model is a matrix containing 2 rows and 2 columns - when you exclude the title rows and columns.
The rows represent either the process or the result of the process.
The columns represent what is being designed - which is either the functionality ("what") to implement, or the structure of ("how") the implementation.
All in all, this 2 x 2 matrix results in these four phenomena:
- The functional design process.
- The functional specification.
- The structural design process.
- The structural design.
The functional design process results in a functional specification. The structural design process results in a structural design.
Functional Design vs. Structural Design
I used to believe that the software design process is one, unified process - that the functional specification is fed into the structural design process in order to produce the final structural design - as illustrated here:
However, the more I think about it, the more I have come to believe that this is not representative of how software design is actually done.
Instead I have come to believe that functional design and structural design are almost disconnected. Not entirely, but a major part of the structural design is decided independently of the functional specification.
Think about it for a moment. There are many ways to implement the same functionality. Many designs that can provide the same functionality. Which of these designs you choose is rarely dependent on the functional specification.
Rather, a big part of the structural design is decided based on non-functional design goals, such as increased development speed, decreased development costs or other non-functional software qualities such as performance, reliability, scalability etc. The diagram below illustrates how I believe software design is performed in practice.
This diagram shows that the majority of the structural design goals come from non-functional design goals. The part of the structural design goals that come from the functional specification would probably be around aspects such as user interfaces, public APIs and other types of interfaces.
Function is Abstract - Structure is Concrete
The way I see it, the functional specification is abstract, whereas the structural design is concrete. Put differently, the functional specification only tells in abstract terms what functionality should be implemented, and the structural design says in concrete terms how that functionality is to be implemented.
Of course there is a gray zone between functional and structural design. For instance, software interfaces such as user interfaces or APIs can be categorized as either functional or structural designs. It depends a bit on how hard the organization needs a very specific design in order to serve their functional goals.
Software Design vs. Software Architecture
I am not aware of any clear distinction between software design and software architecture. Personally, I distinguish between design and architecture in the following way:
Software design is about the structure of software within a single process. In other words, the structure of your code within a single application or service etc.
Software architecture is about the structure of all the processes that make up a system, including their roles, interactions, protocols, data exchange protocols etc. In other words, software architecture is about the structure of two or more processes (applications or services) including their interactions, communication protocol etc.
In short, software design deals with the lower level details and software architecture deals with the higher level details (the big picture) - according to my opinionated distinction. The following diagram attempts to illustrate this distinction:
Design Choices Are Trade-offs
Since software design is about deciding how to structure your code - software design is about making design choices between different design options.
Each design option has its own pros and cons. Thus, making a decision between different the different design options means making a trade-off between the pros and cons of the various design options.
The pros and cons of a design option may or may not be relevant in your particular system and / or situation. For instance, a disadvantage (con) of a design option could be lower performance - but if performance is not an issue in your situation - that disadvantage is not relevant in your concrete situation. Similarly, if the advantage (pro) of a design option was higher performance, but performance is not an issue in your situation, then that pro is not relevant in your concrete situation.
The diagram below shows only two options, but quite often there can be several more options than two.
In other words, there are no "right" or "wrong" designs. The benefit of a given design option depends on the software you want to apply it to. It may be a good option for system A, a so-so option fsystem B and a bad option for system C.
Furthermore, the evaluation of design option pros and cons may change over time. Imagine you are developing a SaaS solution. Aspects such as performance and scalability may not be a big issue early in the process, when the system only has a few hundred users. But once the system has millions of users, all of a sudden performance and scalability start to matter more.
How Do We Design Software, Then?
Now that we have a good idea about what software design is about - which is figuring out what functionality to implement and how to structure the implementation - how do we go about actually doing that?
There are several software design philosophies out there - and I am currently also working on my own software design philosophy which I call "Conscious Design".
As mentioned earlier there are several well-known software design philosophies out there already. Note, that I make a distinction between software design and software development. I do not consider Scrum, Extreme Programming, SAFE etc. to be "design" philosophies - but "development methodologies". When I talk about software design philosophies I mean philosophies and methodologies strictly concerned with the design of the software - not the process via which it is developed.
Here are some of the most well-known software design philosophies:
- Structured Programming
- Object Oriented Programming
- Functional Programming
- Data Oriented Programming
- Aspect Oriented Programming
- Clean Code
- Hexagonal Architecture
- Domain Driven Design
Going into detail about all of these design philosophies in this tutorial would take up too much space and time - but I will explore them in more detail in their own tutorials in the future.
Conscious design is my own software design philosophy which I am currently working on. Thus, it is not yet 100% finished, or crystal clear in its definition.
Conscious design is an attempt to focus more on why we make a given design choice, than on learning all the concrete techniques themselves. In other words, conscious design is about understanding the top design goals behind a given design decision, rather than simply remembering and applying a given design technique blindly.
Once you learn the reasoning, the design goals, then you can come up with your own design solutions to meet these goals, instead of relying on learning by heart the techniques of others. Also, you can determine if a given design technique will actually bring any benefit in your current situation, or not.
I will write more on Conscious Design elsewhere.
Beware of Design Doctrines
Finally, I just want to give you a little "warning" with regard to what I call "design doctrines". Sometimes you may read software design advice which reads something similar to:
- You must always do ABC...
- You must never do XYZ...
The formulation of such advice sounds more than doctrines that must be obeyed regardless of the situation.
Design doctrines that simply says "always" or "never" should be regarded with skepticism. In practice, you might actually sometimes abstain from doing ABC, or actually sometimes do ZYX, despite what the doctrines say about them.
Design advice should not come in the form of commands to be adhered to, in my opinion. Rather, I believe that design advice around a certain design option (technique, pattern, structure, algorithm etc.) should explain why that option is good or bad (its pros and cons) - and also when it is bad (situational conditions that increases or decreases either pros or cons).
Software Design Goals
In the section above about Conscious Design I mentioned that the core tenet of that philosophy is to increase your awareness - your consciousness - about why we make the design decisions we make. The reason behind the decision - in other words.
I call these reasons "design goals" - meaning the goals we are trying to achieve with a given design decision. I cover design goals in more detail in this article: Software Design Goals.