Software Engineering: A Modern Approach
1 Additional Design Principles
1.1 Introduction
In this article, we extend the design principles discussed in Chapter 5 with four additional ones:
- CQS: Command–Query
Separation
- DRY: Don’t Repeat
Yourself
- YAGNI: You Aren’t Gonna
Need It
- KISS: Keep It Simple, Stupid
We then conclude with a discussion of orthogonality, an important property in software design.
1.2 CQS: Command–Query Separation
This principle was proposed by Bertrand Meyer, who also introduced
the Open/Closed Principle, which corresponds to the letter O
in
SOLID.
The Command–Query Separation (CQS) principle states that a class should contain two distinct categories of methods:
Commands: methods that return no value (i.e.,
void) but that have side effects. They modify the system’s state—for example, by writing to a database, changing a class attribute, or updating a global variable.Queries: methods that return values but do not change the system’s state.
According to CQS, a method should be either a command or a query, but never both. In other words: a command should not return values; and a query should not have side effects.
Meyer summarizes the principle succinctly:
Asking a question should not change the answer.
The goal is to promote a clean separation between these categories, since queries are inherently safer to use, i.e., by calling them nothing in the system is modified. For this reason, queries are typically easier to understand, call, and test.
More broadly, developers should strive to separate the imperative part (commands) from the functional part (queries) of their code, favoring the latter and minimizing the former.
1.3 DRY: Don’t Repeat Yourself
This principle was first articulated in The Pragmatic Programmer book by David Thomas and Andrew Hunt (1999). It states that:
Every piece of knowledge must have a single, unambiguous, authoritative representation in a system.
A straightforward way to understand DRY is by considering code. A system should not contain two functions, X and Y, that duplicate the same behavior. This leads to unnecessary work and duplicated maintenance effort.
However, DRY applies to any form of knowledge. For example, a comment that merely repeats what the code expresses may be unnecessary.
We also applied this principle when writing our book: although it is available in three formats (HTML, e-book, PDF), all versions are generated from the same Markdown source. Otherwise, even a small correction would need to be applied three times.
Other names for DRY also exist. For example, in The Art of Unix Programming book, Eric Raymond uses the term SPOT (Single Point of Truth).
1.4 YAGNI: You Aren’t Gonna Need It
This principle is widely referenced by practitioners of Extreme Programming (XP). We also mentioned it briefly in [Chapter 2] (https://softengbook.org/chapter2#programming-practices).
Developers often design and implement features that ultimately go unused or are not relevant at the current stage of the system’s evolution. Because it is easy to request features, clients may propose large lists of functionalities, many of which are nonessential or may never be used.
YAGNI advises:
Implement a feature only when you have strong evidence that it will be used. Avoid building functionalities intended only for medium- or long-term needs because they may lose relevance before then.
For example, suppose you are building a food-delivery app and the client requests support for both Portuguese and Spanish, anticipating future expansion. It may be worth discussing whether Spanish support is truly necessary in the initial release.
1.5 KISS: Keep It Simple, Stupid
This acronym appears in many disciplines. In software design, it refers to developers’ tendency to introduce unnecessary complexity.
For example, teams often use, without need, sophisticated architectures based on patterns such as microservices, Hexagonal Architecture, or Clean Architecture. Or they rely excessively on design patterns.
These approaches are not inherently bad, but in smaller systems or in
systems with limited need to evolve, they often do not pay off. It is
like using a cannon to kill a mosquito.
Thus, when designing a system, it is wise to restrain the impulse to adopt overly elaborate solutions. Use them only when necessary.
When KISS is ignored, the result is a solution that suffers from overengineering, that is an excessive and unjustified application of engineering techniques.
1.6 Orthogonality
Two components of a system are orthogonal if changes to one do not affect the other. As a result, they can be combined or replaced freely.
The Pragmatic Programmer book illustrates this property with the following example:
In a well-designed system, the database is orthogonal to the user interface: you can change the interface without affecting the database and replace the database without changing the interface.
For instance, suppose a system supports multiple user interfaces X and multiple databases Y. In an orthogonal system, choosing interface X imposes no constraints on the choice of database Y. Any combination (x, y) is possible. We can think of X and Y as perpendicular axes forming a plane of valid system configurations.
Orthogonality often emerges naturally in well-designed systems whose modules have high cohesion and low coupling, as discussed in Chapter 5.
Exercises
In a CRUD (Create, Read, Update, Delete) application, which operations should be implemented as Commands? Which should be Queries?
Consider the
Stackclass in the following implementation.- Which method violates the CQS principle?
- How would you reimplement this method in a CQS-compliant way?
- Do you think the CQS-compliant approach is worthwhile in this case?
- Which method violates the CQS principle?
Check out the other articles on our site.