Software design translates domain models and requirements into high-level code structures that achieve goals. Its central challenge is controlling accidental complexity: complexity introduced by unjustified difficult solutions, tricky code, or unnecessary abstractions. Design is therefore a creative balancing activity rather than a search for a universally perfect structure. section 1.2 section 1.3
Start with what the system must do and the properties it must have. Functional requirements describe user-facing capabilities; nonfunctional requirements describe qualities such as performance, stability, extensibility, availability, latency, security, and ease of operation. A workable design satisfies both while remaining understandable enough to change without a rewrite. section 1.2.1 section 1.2
Use design to make deliberate trade-offs:
- Keep accidental complexity low, but do not remove useful abstractions merely to make code shorter; too little abstraction can create coupling and harm maintainability. section 1.3
- Prefer simple, deterministic structures where possible, because complexity raises the cost and risk of maintenance. section 1.2 section 1.6
- Organize large systems into independent parts, then refine high-level architecture into low-level implementation details. section 2.1.3
- Use low coupling and high cohesion as general principles for keeping components manageable across design methodologies. section 1.6
Divide and conquer is a recurring design principle. An interface can define a component’s operations, inputs, outputs, and types independently of its implementation. Client code then depends on the contract rather than a concrete subsystem, allowing implementations to change or be substituted. This form of inversion of control improves robustness, readability, and testability while supporting modularity. section 1.3.2 section 1.6
Designing spaceship control
A spaceship-control system must support manual and automatic operation while remaining robust, fault-tolerant, secure, and compatible with varied components. Its design can model engine-control responsibilities through an interface, an engine implementation, and a control-panel client. When a high-accuracy thermometer reports Kelvin but the sensor contract expects Celsius, an Adapter converts the mismatched interface without forcing client code to change. section 1.2.1 section 1.3 section 1.4.2
Object-oriented design provides established patterns and UML diagrams for reasoning about large applications. Functional programming supplies different tools and trade-offs: higher-order functions, lambdas, and closures can provide functionality associated with patterns such as Command or Strategy, but functional code is not automatically simple or loosely coupled. Choose approaches consciously rather than treating a paradigm as a guarantee. section 1.4.2 section 1.2 section 1.6
Figure 1.4 Compromising between simplicity, goals, and requirements
Compare the competing pressures of simplicity, project goals, and requirements when judging whether a proposed structure is justified. section 1.2.1



