About good design
When talking about software, many people either talk about the software architecture or about its implementation. I sometimes have the feeling that software design is a bit overlooked. That would be a pity, because a good design contributes to a high extent to the quality of the software.
Read more: About good designArchitecture, design, implementation
In the previous paragraph I used the words architecture, design and implementation. In some sense, they form a continuum from the abstract to the concrete software. The difference between architecture and design is not very clear for software, just as it is in other domains such as in construction. However, most people feel they are distinct nonetheless. To be clear, I’ll use the definition that states that architecture is the most high level description, focused on the outside, while design is more concrete and practical, focused on getting the architecture to work; it is an intermediate level between architecture and implementation.
To make it more specific:
- The architecture describes the application on a high level. For example, it may define that the product is implemented using a service oriented architecture, or a 3-tier layered design
- The design takes the components defined in the architecture, and defines the classes and interactions that makes each module work.
- The implementation is the code itself.
Importance of design
When changes need to be made to legacy code that is developed as a big ball of mud, people talk often either about architectural changes (“we need to support a new database engine”) or on implementation level (“the unit tests of this class are slow because they involve a lot of database communication”). The design level is often neglected. This is not very strange, because lack of design is what created the legacy code in the first place.
This lack of design is usually the actual problem. Responsibilities are distributed over classes, and classes have too many intertwined responsibilities. This makes the code resistant to change.
Properties of a good design
A good design is therefore valuable:
- It reduces duplication of functionality and fosters reuse. Classes are shaped in such a way that they are also fit for reuse in unforeseen situations. This makes it easy to extend the program with new features.
- It reduces the chances of implementation errors. Variations in behavior are resolved using polymorphism, so that the using code does not need to be littered with
if..else
constructs all over the place. Whatever client code needs to be done, it can be done in only one way and that is the correct way. - It makes the implementation trustable. You know from the interface what a class does, because it has exactly one responsibility. Compare this to a situation of legacy code where it is not clear what happens and why: in the latter case, it is much more cumbersome to make changes. So good design speeds up development.
A good design has many characteristics of the SOLID principles. It uses design patterns where appropriate.
Towards a good design
When developing software, it really pays to think about design starting from day 1. It helps to think of design not as something static, but as something that develops together with the product. That is, it may look like the program has a good design, then a feature request comes up that just does not fit. At such occasions, rethink the design. Often some generalization can be done to refactor towards a new design which fits the new situation.
When working on legacy software, it helps to find out about software antipatterns: design constructs that have a negative impact on the code quality. There are lists of these code smells that tell why you shouldn’t use them, and how to resolve these. It also helps to identify abstraction layers that may server as refactoring goals.