Commonly occuring good advice aimed at improving maintainability of source code and other technical documents.
- Avoid duplication, tautology, repetition, duplication, and saying the same thing over and over in different ways.
- Everything (words, statements, comments) should have a clear purpose.
- Avoid complexity.
- Encourage simplicity.
-
Simple Made Easy, Rich Hickey (2011)
Simplicity is contrasted with both complexicy and ease in the context of software design.
-
C++ Seasoning, Sean Parent (2013)
The first half of the talk, No Raw Loops, is a compelling demonstration of methods which dramatically simplify code.
- Command–query separation
- KYSS (Keep Your Stuff Separate), Tony Van Eerd
- Keep scopes small, C++ Core Guidelines
Modularity extends far beyond computer program modules or translation units, and includes the organisation, contents within and relationships between:
-
Functions
"It is better to have 100 functions operate on one data structure than 10 functions on 10 data structures." Epigrams on Programming, Alan J. Perlis, 1980
-
Objects
-
Source files
-
Packages
-
Documents
-
Standards and Conventions
-
Projects
-
Teams
Many well-meaning projects store toolchain configuration in their build system description. Here's an example of a C++ project combining the concerns of configuring a specific compiler, with describing build targets. This is very common because CMake makes it easy to do. In reality, a CMakeLists.txt file should never contain a compiler flag because this couples the description of what targets to make (a build system concern) with what flags to pass when building (a toolchain concern). The result is a brittle configuration which breaks immediately when a novel toolchain is used.
Within any organisational unit cohesion is increased where lower-impact decisions (formatting, naming, and paradigm conventions) are applied consistently throughout.
Individual units may be constituent parts of a larger unit, e.g. multiple package dependencies in a project. The sub-units should be free to make and apply contrary decisions, otherwise coupling ensues.
- Avoid cycles in your graphs.
- Prefer iteration to recursion. (See, e.g. AV Rule 119 of Joint Strike Fighter Air Vehicle C++ Coding Standards, "Functions shall not call themselves, either directly or indirectly (i.e. recursion shall not be allowed).")
- CI pipelines which are triggered by pushes, and also cause pushes (see GitLab guidelines).
If something disappointing happens:
- fail
- don't try to muddle on
- don't try to be 'helpful' to get around the problem with the addition of complexity.
- When a
std::map::operator[]lookup failed to find an element matching the given key, it creates one instead. This leads to a cascade of additional complexity in the design, and makes the API less reasonable in the case that the looking is successful.
Most poor solutions in production are the author's first attempt at a solution.
- "Design it Twice", Chapter 11 of A Philosophy Of Software Design, John Ousterhout
-
Don't Repeat Yourself / Avoid Tautology, i.e. describe the pattern; don't be the pattern
-
When tautology exists in the domain, model it, e.g.:
- if a model uses "ATM Machine", don't be afraid to write a variable,
automatic_teller_machine_machine. This isn't tautology in the nameing, merely a reflection of unavoidable tautology in the system. - if a library reuses names, e.g.
fmt::format,string::string, orranges::range, in a tutological name, don't try to avoid this tautology when you model around it. - If Sarah of Sarah's Sandwiches has a nametag, it's path might be "/Sarah's Sahdwiches/Sarah/Sarah's nametag.svg". Given the nametag files might be copied to a common location, this is either not tautological, or perferable to a DRYer filename.
- if a model uses "ATM Machine", don't be afraid to write a variable,
- "Symmetry is a complexity reducing concept ...; seek it everywhere", Epigrams on Programming, Alan J. Perlis, 1982
- Command / Query - goes under UNIX way: each thing does only one thing