Design principles are the guiding beliefs that inform architectural decisions. They're not rigid rules β€” they're heuristics that help you make better trade-offs. Knowing when to apply a principle (and when to bend it) separates good architects from great ones.

4.1 SOLID Principles

Originally formulated for object-oriented design by Robert C. Martin, the SOLID principles apply equally well at the architectural level β€” to modules, components, and services.

S β€” Single Responsibility Principle (SRP)

"A module should have one, and only one, reason to change."

This doesn't mean a module does only one thing β€” it means it serves only one stakeholder or business capability. If a component handles both billing logic and email formatting, changes to email templates could break billing calculations. They should be separate.

At the architecture level, SRP means each building block should have a clear, focused purpose. A "UserService" that handles authentication, profile management, billing, and email sending violates SRP at the service level.

O β€” Open/Closed Principle (OCP)

"Software entities should be open for extension, closed for modification."

You should be able to add new behavior without changing existing code. In architecture, this means designing extension points: plugin systems, strategy patterns, event-driven hooks. When a new payment method is added, you write a new adapter β€” you don't modify the core payment processing logic.

L β€” Liskov Substitution Principle (LSP)

"Subtypes must be substitutable for their base types without altering the correctness of the program."

At the architecture level, this means any component that implements an interface must behave consistently with that interface's contract. If you swap a PostgreSQL repository for a MongoDB repository, the service using it shouldn't break β€” both must honor the same behavioral contract.

I β€” Interface Segregation Principle (ISP)

"Clients should not be forced to depend on interfaces they do not use."

Instead of one large, general-purpose interface, provide several smaller, specific ones. A service that only needs to read orders shouldn't depend on an interface that also includes creating and deleting orders. This reduces coupling and makes the system easier to evolve.

D β€” Dependency Inversion Principle (DIP)

"High-level modules should not depend on low-level modules. Both should depend on abstractions."

This is arguably the most important principle for architecture. Instead of the business logic depending directly on the database, both depend on an interface (a "port" in hexagonal architecture). This allows you to swap implementations without touching the business logic.

❌ Without DIP Business Logic depends on PostgreSQL Repo βœ… With DIP Business Logic Β«interfaceΒ» Repository PostgreSQL Repo
DIP is frequently tested. Remember: both the high-level and low-level modules depend on the abstraction. The abstraction is owned by the high-level module (the domain), not the low-level module (the infrastructure).

4.2 Coupling and Cohesion

These are the two most fundamental metrics for evaluating any decomposition. They apply at every level: classes, modules, components, services.

Goal: LOW coupling between building blocks + HIGH cohesion within each building block. This is the single most important design heuristic in software architecture.

Types of Coupling (worst β†’ best)

❌ Content CouplingModule modifies internals of another Common CouplingShared global data Control CouplingModule controls flow of another (flags) Stamp CouplingShared data structure, only parts used Data CouplingOnly necessary data via parameters βœ… Message CouplingCommunication only via messages/events ← WORST BEST β†’

Types of Cohesion (worst β†’ best)

❌ CoincidentalRandom, unrelated elements grouped LogicalRelated by category (all I/O in one module) TemporalRelated by time (all init code together) ProceduralSteps of a single procedure CommunicationalOperate on the same data SequentialOutput of one part = input of next βœ… FunctionalAll elements contribute to a single, well-defined task ← WORST BEST β†’

4.3 Other Essential Principles

PrincipleMeaningPractical Implication
Separation of ConcernsEach module addresses one distinct concernDon't mix logging with business logic, or UI with persistence
Information Hiding (Parnas)Hide internal details; expose only what's necessaryModules communicate through interfaces, never by reaching into each other's internals
DRYDon't Repeat Yourself β€” avoid duplicating knowledgeBut beware: forcing DRY across service boundaries can create coupling. Sometimes duplication is preferable to the wrong abstraction.
KISSKeep It Simple, StupidPrefer the simplest solution that works. Complexity should be justified by actual needs, not speculative future requirements.
YAGNIYou Aren't Gonna Need ItDon't build abstractions or features until they're actually needed. Premature generalization is a common source of unnecessary complexity.

When Principles Conflict

In real systems, principles often tension against each other. DRY says "don't duplicate," but applying DRY across microservices creates coupling. KISS says "keep it simple," but DIP adds indirection layers. OCP says "extend without modifying," but YAGNI says "don't build extension points you don't need yet."

The skill of architecture is navigating these tensions β€” knowing which principle to prioritize in which context, and being explicit about the trade-off you're making.