Simple Design Rules
Whether you are designing systems or individual modules, never forget to use the simplest thing that can possibly work.
A design is “simple” if it follows these rules:
- Runs all the tests
- Contains no duplication
- Expresses the intent of the programmer
- Minimizes the number of classes and methods
A design must produce a system that acts as intended. A system might have a perfect design on paper, but if there is no simple way to verify that the system actually works as intended, then all the paper effort is questionable.
A system that cannot be verified should never be deployed.
We need to have tests and run them continuously impacts our system’s adherence to the primary OO goals of low coupling and high cohesion. Writing tests leads to better designs.
- Classes are small and single purpose. It’s easier to test classes that conform to the SRP.
- Tight coupling makes it difficult to write tests. We use principles like DIP and tools like dependency injection, interfaces, and abstraction to minimize coupling.
Once we have tests, we are empowered to keep our code and classes clean. We do this by incrementally refactoring the code.
During this refactoring step, we can apply anything from the entire body of knowledge about good software design. We can increase cohesion, decrease coupling, separate concerns, modularize system concerns, shrink our functions and classes, choose better names, and so on.
This is also where we apply the final three rules of simple design: Eliminate duplication, ensure expressiveness, and minimize the number of classes and methods.
Duplication is the primary enemy of a well-designed system. It represents additional work, additional risk, and additional unnecessary complexity.
Duplication manifests itself in many forms:
- Lines of code that look exactly the same.
- Lines of code that are similar to.
- Duplication of implementation.
Code should clearly express the intent of its author. The clearer the author can make the code, the less time others will have to spend understanding it. This will reduce defects and shrink the cost of maintenance.
Spend a little time with each of your functions and classes express yourself by choosing better names, splitting large functions into smaller functions, using design pattern, and generally just take care of what you’ve created.
Well-written unit tests are also expressive. A primary goal of tests is to act as documentation by example. Someone reading our tests should be able to get a quick understanding of what a class is all about.
But the most important way to be expressive is to try. All too often we get our code working and then move on to the next problem without giving sufficient thought to making that code easy for the next person to read. Remember, the most likely next person to read the code will be you.
This rule suggests that we also keep our function and class counts low.
High class and method counts are sometimes the result of pointless dogmatism. It requires experience, open mind, and reason to apply principles correctly in its true purpose and the proper context of application.
Our goal is to keep our overall system small while we are also keeping our functions and classes small. Remember, however, that this rule is the lowest priority of the four rules of Simple Design. So, although it’s important to keep class and function count low, it’s more important to have tests, eliminate duplication, and express yourself.