Architecture Principles
Foundational design principles that guide architectural decisions — separation of concerns, loose coupling, high cohesion, and the SOLID principles applied at the system level.
What it is
Architecture principles are a set of established guidelines and heuristics that help architects and engineers make consistent, defensible decisions when designing systems. Unlike patterns — which are reusable solutions to specific problems — principles are universal truths that apply across nearly every architectural context. They act as a shared decision-making framework for teams, reducing the cost of each individual design choice by anchoring it to an agreed-upon set of values.
Principles operate at two levels: the code level (e.g., the SOLID principles as originally stated by Robert C. Martin) and the system level (e.g., loose coupling between services, design for failure). While code-level principles are well-known, system-level principles are often left implicit, which causes inconsistency across a large codebase or a multi-team organization.
Why it exists
Without explicit principles, teams make ad hoc decisions that feel locally correct but are globally inconsistent. One team might optimize for simplicity while another optimizes for flexibility, resulting in a system where neither goal is well served. Architecture principles make the value trade-offs explicit and durable — they survive team turnover, re-orgs, and changing product priorities.
Principles also accelerate onboarding. When a new engineer asks "why is this structured this way?", a well-documented principle provides a meaningful answer tied to business context, not just "because we've always done it this way." They are also the mechanism through which senior architects scale their influence: rather than reviewing every decision, they articulate principles and trust teams to apply them.
When to use
- When establishing a new engineering organization or team and codifying the design culture from day one.
- When onboarding multiple new engineers who need a shared vocabulary for design discussions.
- When architectural debt is accumulating because teams are making inconsistent decisions without a shared framework.
- When conducting architecture reviews — principles provide explicit criteria against which proposals can be evaluated.
- When resolving design disputes; a documented principle depersonalizes the argument and anchors it in shared organizational values.
When not to use
- Do not treat principles as inviolable laws — every principle has exceptions, and refusing to acknowledge trade-offs leads to dogmatic design.
- Do not create so many principles that teams cannot remember them; six to ten well-chosen principles outperform thirty rarely-consulted ones.
- Do not apply system-level principles blindly to performance-critical hot paths where micro-optimization genuinely requires coupling concerns (e.g., tight GPU compute loops).
- Do not confuse principles with standards; a principle is "prefer stateless services," a standard is "all HTTP services must return errors using RFC 7807 Problem Details."
Typical architecture
The most widely applied architecture principles form a hierarchy: universal software engineering principles underpin system-design principles, which in turn shape specific architectural decisions. The diagram below illustrates how the core principles interact during the design of a new system capability.
Business Constraints
(cost, time, team skills)
│
▼
┌───────────────────────────────────────────────────┐
│ DESIGN PRINCIPLES FILTER │
│ │
│ SRP ──► Each module has one reason to change │
│ OCP ──► Extend via new code, not by editing old │
│ LSP ──► Subtypes honour contracts of supertypes │
│ ISP ──► Prefer narrow, focused interfaces │
│ DIP ──► Depend on abstractions, not concretions │
│ │
│ SoC ──► Separate UI / domain / data concerns │
│ LC ──► Minimise inter-module dependencies │
│ HC ──► Keep related things together │
│ DRY ──► Single source of truth for each concept │
│ YAGNI──► Don't build what isn't needed yet │
│ D4F ──► Assume every remote call can fail │
└───────────────────────────────────────────────────┘
│
▼
Architectural Decisions
(service boundaries, data stores, communication)
Pros and cons
Pros
- Reduces cognitive load on individual decision-makers by providing a consistent framework.
- Scales architectural thinking across teams without requiring a central gatekeeper.
- Makes design reviews faster and less contentious by providing objective criteria.
- Improves long-term maintainability when high-cohesion/loose-coupling principles are followed consistently.
- Provides a clear onboarding foundation for new engineers joining the team.
Cons
- Principles can become dogma if teams are not empowered to challenge and evolve them.
- Poorly articulated principles ("keep it simple") are too vague to be actionable.
- Over-adherence to DRY at the system level creates unwanted coupling between otherwise independent services.
- Maintaining a living set of principles requires ongoing effort and governance.
Implementation notes
SOLID at the system level: The Single Responsibility Principle applied to services means each service owns one business capability and one data store. The Open/Closed Principle applied to APIs means you add new endpoints or fields rather than changing existing contracts. The Dependency Inversion Principle applied to service communication means depending on published interfaces (API contracts, event schemas) rather than internal implementation details of another service.
Loose coupling and high cohesion: These two principles are complementary. High cohesion means things that change together live together — placing unrelated functionality in the same module just because it is "convenient" is a cohesion violation. Loose coupling means changes in one module do not cascade to others — shared databases, shared libraries with business logic, and synchronous call chains are all coupling mechanisms to be managed deliberately.
Design for failure: Every remote call, disk write, and network packet can fail. Architect every integration point with timeouts, circuit breakers, retries with exponential back-off, and fallback behavior. The principle of design for operability extends this: every system should make its health, performance, and errors observable by default.
YAGNI and the cost of speculative generality: Premature abstraction is one of the most expensive forms of technical debt. Build what is needed for today's known requirements and leave extension points where the domain analysis strongly suggests future variation — not everywhere "just in case."
Common failure modes
- Principle overload: Thirty principles on a wiki page that nobody reads. Pare down to the six to ten most relevant for your context.
- DRY misapplied across service boundaries: Extracting shared business logic into a common library that all services depend on — this creates the coupling that microservices are designed to avoid.
- SRP interpreted too narrowly: Taking single responsibility to mean every function does one line of work, producing a fractal of tiny abstractions that are impossible to navigate.
- OCP as an excuse for complexity: Adding layers of abstraction to avoid touching existing code when a simple in-place change would be clearer and safer.
- Principles without rationale: Documenting "prefer asynchronous communication" without explaining why leads teams to apply it in situations where synchronous calls are obviously correct.
Decision checklist
- Have we documented our top 6–10 principles and the rationale behind each?
- Does each proposed service boundary satisfy single responsibility — one business capability, one reason to change?
- Have we identified all coupling points (shared databases, shared libraries with domain logic, synchronous chains) and made them explicit trade-offs?
- Do our interfaces depend on abstractions (API contracts, event schemas) rather than implementation details of other services?
- Have we validated that every feature we are building is driven by a current requirement, not a speculative future one (YAGNI)?
- Does every remote integration point have timeout, retry, and circuit-breaker behavior defined?
- Are our principles stored in version control alongside the code and reviewed periodically?
- Can a new team member explain why a key architectural decision was made using one of the documented principles?
Example use cases
- A new e-commerce platform uses SRP to define separate services for catalog, inventory, orders, and payments — each team owns one capability end-to-end.
- A financial institution codifies "design for auditability" as an explicit principle, requiring every state-changing operation to produce an immutable event log entry.
- A SaaS company enforces the Dependency Inversion Principle by requiring all inter-service communication to go through versioned OpenAPI contracts stored in a central schema registry.
- A gaming backend applies YAGNI to avoid building a general-purpose rules engine until the product team identifies three distinct rule categories that justify the abstraction.
Related patterns
- Quality Attributes — principles operationalize quality attribute requirements such as maintainability and modifiability.
- Architectural Trade-off Analysis — structured methods for resolving conflicts between competing principles.
- Architecture Decision Records — the mechanism for capturing when and why a principle was applied or overridden.
- Bounded Contexts — SRP and high cohesion at the domain level.
Further reading
- Clean Architecture — Robert C. Martin — the canonical source for SOLID principles and their system-level applications.
- Architecture Principles in Practice — Architecture Notes — practical guidance on defining and communicating principles.
- Principles of Microservices — InfoQ — how universal principles apply to service-oriented architectures.