Reading path for Core Developer Skills
Knowing the qualities that define the nature of the code that you seek is useful, but we also need to know the underlying principles that will, most reliably, lead us to those qualities. Also, principles will help to keep us aligned with qualities when unusual, unforeseen, and unfamiliar problems arise. Principles are general guidance about creating good software; there is no “end” to following a principle, you could always “do more”… but in following them generally we find that everything gets incrementally easier to change, scale, and extend.
Here are principles that should be followed when creating software:
Encapsulate by policy, Reveal by need
Design is about making decisions. Once such decision is often “should I encapsulate this, or not?” We make this decision in the full knowledge that we may be making it incorrectly.
However, it becomes quickly clear that:
Put another way, it is always easier to remove encapsulation from an existing system than to add it. Therefore, being “over-encapsulated” is a much stronger position than being “under-encapsulated.”
Watch: Encapsulating Construction
The Open-Closed Principle
When designing systems, we prefer to leave them “Open for Extension,” but “Closed to Modification.”
Ivar Jacobsen: “All systems change during their life cycles. This must be borne in mind when developing systems expected to last longer than the first version”
Bertrand Myer: “Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”
Another way of saying this is: in design, try to set things up so that when new requirements are added, you can accommodate them by adding a new class, method, or other entity, while leaving the existing classes, methods, etc… as they are. It represents an acknowledgement that adding new code is less risky and wasteful than modifying existing code.
This is normally expressed as a point of view that influences up-front design decisions, but in fact can also guide the emergence of a design through refactoring.
Separate use from construction
Good design introduces layers of separation between entities. We often, for example, seek to separate what is likely to change from that which is not. When we separate concerns in design we also promote strong cohesion, because the resulting entities do fewer things for themselves.
One example of this is to separate those entities that are “users” of other entities (often called “clients”) from the builders, or instantiators of those same entities. The user of an entity is vulnerable to changes in its interface, but can be abstractly coupled to its type. The builder of an entity must be coupled to its type, but not its interface.
Shalloway’s Law and Shalloway’s Principle
Al Shalloway, President and CEO of Net Objective, has a principle that guides him:
If you have to find more than one thing when a change is made, you will miss something. Shalloway’s Principle says to avoid situations where Shalloway’s Law applies.
If using object-oriented programming, then the proper use of object-orientation and encapsulation means that you never have to find more than one thing when a change is required.
The Single Responsibility Principle
Creating long-term value in software is all about responding to change. Systems that mix multiple responsibilities into a single entity create difficulties because:
Entities are hard to name in a way that reveals everything they do Entities are hard to test because all combinations of their different responsibilities have to be tested Systems are poorly encapsulated because within an object encapsulation is not enforced by the compiler
Therefore, each entity (typically class) in a system should have a single responsibility, and everything it contains should be related to it.