Architecture Foundations

📖 6 min read

Core Laws of Software Architecture

  1. Everything in software architecture is a trade-off
  2. Why is more important than how
  3. Most decisions exist on a spectrum, not binary choices

– Mark Richards & Neal Ford, Fundamentals of Software Architecture (2020)

These three laws form the foundation of architectural thinking. The first law reminds us that every decision involves compromise; choosing one quality attribute often means sacrificing another. The second emphasizes that understanding the reasoning behind decisions matters more than memorizing implementation details. The third challenges binary thinking and encourages viewing decisions as contextual choices along a continuum.

What Is Software Architecture?

Software architecture encompasses five interconnected dimensions:

Structure defines how the system is organized: the components and their relationships. Think of this as the blueprint showing what pieces exist and how they connect.

Characteristics specify the non-functional capabilities the system must deliver, such as scalability, performance, security, and maintainability. These drive architectural decisions more than functional requirements do.

Components are the behavioral building blocks of the system. Each component has a defined responsibility and encapsulates related functionality.

Style describes the overall implementation pattern and topology, such as layered, microservices, or event-driven architecture. The style determines how components interact and deploy.

Decisions establish the rules and constraints that guide how the system is constructed. These include technology choices, coding standards, and governance policies.

Together, these five dimensions create a complete picture of the system’s architecture. Neglecting any dimension leads to incomplete architectural thinking.

Characteristics drive architectural decisions more than functional requirements do.

Architectural Thinking

Architecture vs Design

Understanding the boundary between architecture and design helps clarify responsibility and scope. Architecture deals with strategic, long-term decisions that are hard to change and involve significant trade-offs. Design focuses on tactical, short-term decisions that are easier to change and involve fewer trade-offs.

When evaluating whether a decision is architectural or design-level, ask yourself: How much planning does this require? How many people are affected by this choice? Does this serve a long-term vision or solve an immediate problem?

Consider database selection. Choosing between a relational database and a document store is architectural because it affects how the entire system stores and retrieves data, influences team skills required, and is expensive to reverse. Deciding on specific table indexes is design because it’s localized, easier to change, and affects fewer people.

The distinction isn’t always clear-cut, but recognizing the difference helps architects focus their energy on decisions with lasting impact.

Technical Breadth vs Depth

Architects must develop technical breadth rather than depth. Breadth means knowing a little about many technologies, patterns, and domains. Depth means expert-level knowledge in a narrow area. While developers benefit from deep expertise in specific technologies, architects need broad awareness across the technology landscape.

The goal is to move technologies and concepts from “unknown unknowns” (things you don’t know you don’t know) into “known unknowns” (things you know exist but don’t deeply understand). When a decision requires deep knowledge, you can then invest time to move that specific area into “knowns.”

Breadth means knowing a little about many technologies, patterns, and domains. Depth means expert-level knowledge in a narrow area.

Common Dysfunctions

Trying to maintain expertise in too many areas leads to burnout. You cannot be an expert in everything. Accept that breadth means surface-level knowledge in most areas.

Stale expertise occurs when outdated knowledge is treated as current. An architect with deep Java experience from 2010 may not recognize how modern Java has evolved.

Frozen Caveman antipattern describes reverting to irrational concerns based on past trauma instead of objective assessment.

Core Architectural Skills

Trade-Off Analysis

“Programmers know the benefits of everything and the trade-offs of nothing. Architects need to understand both.” – Rich Hickey

Every architectural decision involves trade-offs. Choosing microservices over a monolith trades simplicity for scalability. Choosing eventual consistency over strong consistency trades immediate correctness for availability. There is no universally correct answer; the right choice depends on context.

When evaluating trade-offs, consider the environment, business drivers, organizational culture, budget constraints, delivery timelines, and team skills. What works for Netflix may not work for a ten-person startup. What works in finance may not work in e-commerce.

Business Translation

Architects must translate business requirements into architecture characteristics. When stakeholders say “the system must be reliable,” that translates into specific architectural decisions about redundancy, failover, monitoring, and recovery procedures. When they say “we need to scale rapidly,” that informs decisions about horizontal scalability, stateless design, and distributed architecture.

Common business justifications map to architectural priorities:

  • Cost concerns drive decisions about cloud vs. on-premises, serverless vs. containers, and build vs. buy
  • Time to market pressures favor simpler architectures, existing platforms, and reduced customization
  • User satisfaction translates to performance, availability, and user experience characteristics
  • Strategic positioning influences decisions about vendor lock-in, open standards, and long-term flexibility

Hands-On Coding Balance

Architects must stay technically current through hands-on coding, but they must avoid the Bottleneck Trap. When architects own critical-path code, they become blockers. Every feature waits for the architect’s availability, and delivery velocity drops.

Recommended Approach

Architects should delegate framework code to senior developers and focus their coding efforts on non-critical areas: proof-of-concepts, technical debt reduction, bug fixes, automation tooling, and code reviews. Working on features one to three iterations ahead of the main team keeps architects connected to the codebase without blocking progress.


Partitioning Strategies

When organizing system components, architects face a fundamental choice: partition by technical capabilities or by business domains. This decision profoundly affects team structure, communication patterns, and long-term maintainability.

Technical Partitioning

Organizes the system by technical capabilities: presentation layer, business logic layer, and persistence layer. Each layer contains code related to a specific technical concern, regardless of which business domain it serves.

Advantages: Clear technical separation, aligns with traditional layered architecture patterns, developers with specialized skills can focus on their areas.

Trade-offs: High global coupling, single business feature requires changes across all layers, domain concepts scatter across layers, difficult to migrate to distributed architecture.

Domain Partitioning

Organizes the system by business domains or workflows, following Domain-Driven Design principles. Each partition contains all the technical layers needed to deliver business capability within a specific domain.

Advantages: Models the business more naturally, supports cross-functional teams owning vertical slices, natural service boundaries for distributed architecture.

Trade-offs: Customization code appears in multiple places, maintaining consistency requires discipline, shared standards needed across domains.

Conway’s Law

“Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure.”

– Melvin Conway (1967)

Conway’s Law observes that team structure directly influences system architecture. If you organize teams by technical specialty (frontend team, backend team, database team), you’ll likely build a technically-partitioned, layered architecture. If you organize teams by business domain (checkout team, inventory team, payments team), you’ll likely build a domain-partitioned architecture.

This happens because people design systems that reflect their communication patterns. Frontend and backend developers who rarely talk will build systems with rigid boundaries between presentation and business logic. Cross-functional teams that collaborate daily will build more integrated vertical slices.

People design systems that reflect their communication patterns.

The Inverse Conway Maneuver deliberately structures teams to encourage the desired architecture. If you want a domain-partitioned architecture, organize cross-functional teams around business domains first. The architecture will follow naturally.


Team Topologies

Framework by Matthew Skelton and Manuel Pais from “Team Topologies” (2019)

Team organization directly affects architecture through Conway’s Law, but not all team structures are equal. Matthew Skelton and Manuel Pais identified four fundamental team types that optimize software delivery:

Stream-Aligned Teams

Align to a flow of work from a business domain. They focus on a single business domain, moving quickly to deliver discrete value. This is the primary team type in most organizations.

Examples: checkout team, inventory team, customer service team

Enabling Teams

Bridge capability gaps across stream-aligned teams. They provide research, learning, and specialized knowledge to help teams overcome obstacles.

These teams offer temporary assistance rather than creating permanent dependencies.

Complicated-Subsystem Teams

Build and maintain systems requiring specialized knowledge that would overwhelm stream-aligned teams.

Examples: video processing engines, mathematical algorithm libraries, real-time trading systems

Platform Teams

Provide internal products that accelerate stream-aligned teams. They build self-service APIs, tools, and services that form the foundation other teams build upon.

They treat other teams as customers, providing product experiences that make stream-aligned teams more productive.

Found this guide helpful? Share it with your team:

Share on LinkedIn