Design principle
Design principles are foundational guidelines that inform decision-making in software design and architecture. Design principles offer heuristics rather than rigid rules. They make generalizations about approaches that tend to work and those that tend to fail.
Well-known design principles include high cohesion, low coupling, conceptual integrity, the single-responsibility principle, and the separation of concerns. Not all design principles are named and most can be reduced to four core principles of software architecture.
- Avoid rigidity. A component is difficult to change when every change potentially affects many other components.
- Avoid fragility. A system is fragile when changes to a component lead to unexpected results, defects, or otherwise negative consequences in other, unrelated components.
- Aoid immobility. A component is difficult to reuse because it cannot be easily disentangled from other components of its parent module or system.
- Avoid viscosity. A rigid, fragile, immobile system causes friction in the software delivery process, making it slow to implement and ship changes.
Unlike design patterns, which describe reusable solutions to specific recurring problems, design principles operate at a higher level of abstraction. They don’t prescribe a particular structure or mechanism. Instead, they shape the values and priorities that guide our selection and application of patterns. A design principle expresses a preference and helps to reason about trade-offs when concerns pull in different directions.
For example, the DRY principle argues that duplication is a source of errors and maintenance burden, but it doesn’t specify how to factor out the duplication. The KISS principle names simplicity as a design goal and pushes back against the impulse to over-engineer, but offers no concrete guidance to achieve this.
Many design principles are interrelated and mutually reinforcing. Cohesion and coupling, for example, are complementary principles, since modules that group strongly related responsibilities (high cohesion) tend to depend less on other modules (low coupling). Similarly, Isolation and redundancy are complementary approaches to fault tolerance. One limits the blast radius of failures, the other ensures that a failed component has a backup.
Applied dogmatically, any principle can lead to over-engineering or unnecessary complexity. The DRY principle, taken too extremes, famously has this effect. The Pareto principle is a useful reminder that a principle need not be followed perfectly to deliver its value.
See also laws and principles that apply at the scale of teams, projects, and organizations, rather than statements, classes, and modules, such as Conway’s law and Parkinson’s law.
Index of design principles
- Acyclic dependencies
- Code for the maintainer
- Cohesion
- Common closure
- Common reuse
- Conceptual integrity
- Coupling
- Dependency inversion
- Domain-driven design
- Encapsulate what changes
- Information hiding
- Isolation
- KISS
- Package principles
- Pareto principle
- Redundancy
- Reuse/release equivalence
- Single-responsibility
- SOLID
- Stable abstractions
- Stable dependencies
- Unix philosophy