Design principles

Design principles are foundational guidelines that inform decision-making in software design. They codify hard-won lessons about what tends to work and what tends to fail, offering heuristics rather than rigid rules.

A design principle expresses a preference – eg. for cohesion over coupling – and helps teams reason about trade-offs when competing concerns pull in different directions.

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 the selection and application of patterns. 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 – that is left to the designer’s judgment.

Many design principles are interrelated and mutually reinforcing. Cohesion and coupling are two sides of the same coin. Modules that group strongly related responsibilities tend to depend less on other modules. Separation of concerns and modular design both push toward decomposing a system into parts that can be reasoned about independently. 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.

Some principles are associated with particular domains or methodologies. The Unix philosophy is a set of design principles for building composable command-line tools. Domain-driven design advocates principles such as aligning software structure with bounded contexts. The SOLID principles, of which dependency inversion is one, guide object-oriented design at the class and module level.

Principles are most useful when they are understood in context. Applied dogmatically, any principle can lead to over-engineering or unnecessary complexity. The Pareto principle is itself a useful reminder: a principle need not be followed perfectly to deliver most of its value.