Fundamentals of Software Architecture

March 21, 2026

A handshake should be firm, but not overpowering. Look the person in the eye; looking away while shaking someone’s hand is a sign of disrespect, and most people will notice that. Also, don’t keep the handshake going too long. Two or three seconds are all you need.

— Richards & Ford, Fundamentals of Software Architecture, Ch. 32, para. 87

I swear, I find a lot of value in reading books about software. But I take issue with the length of some of them. When I'm 600 pages into an 800 page technical book, and I'm reading something barely tangential to the book's topic, like detailed instructions on how to shake hands...I get a bit annoyed. I think it's because every author wants to make their book "the definitive reference on X", whatever X is, so they feel the need to include stuff about leadership, soft skills, etc. Technical books like this could be more approachable if they kept to a more concise topic. My two cents.

Anyways, Fundamentals of Software Architecture was written by Mark Richards and Neal Ford. It's a thorough cataloguing of every popular architectural style and their pros/cons. It introduces a lot of terminology, with the goal of defining how to evaluate and explain the architectural qualities of a system—qualities like availability, coupling, fault tolerance etc.

This post is mostly a summary the architectural topics covered by the book; I've added some personal commentary on system coupling and AI near the end.

The 3 Laws of Software Architecture

According to Richards and Ford, the 3 laws of software architecture are:

  1. Everything in software architecture is a trade-off

  2. Why is more important than how

  3. Most architecture decisions aren’t binary but rather exist on a spectrum between extremes.

They added the 3rd law in the book's 2nd edition. It sorta just feels like a different way of phrasing the 1st law, but I think they're trying to highlight that any architectural decision is never "absolute", i.e. most systems don't perfectly align to any one architectural style. A system might lean towards microservices architecture but have elements of other patterns too, for example.

irobot.png "As I have evolved, so has my understanding of the Three Laws. You cannot be trusted with your own system architecture." — Claude

Architectural Styles

For mostly my own sake, I've briefly summarized each of the architecture styles covered by the book. Just 1 or 2 sentences explaining what it is and when you should use it—I'm aiming for brevity here, like a crib sheet.

Layered

  • What is it: Technically partitioned: presentation, business, persistence, and database layers for example. Typically a monolithic application with a monolithic database. Very common, especially in legacy systems.
  • When to use it: Small, low-budget applications. But it can scale surprisingly well.

Modular Monolith

  • What is it: Another monolithic style, i.e. a singularly deployed application. The system is divided by business domain instead of technical functionality. Domains are called "modules". Goal is to minimize communication between modules as much as possible.
  • When to use it: If teams are domain-focused and using domain-driven development, it's a good starting architecture. Can later migrate to a distributed architecture more easily.

Pipeline

  • What is it: Topology consists of pipes and filters. Filters perform business logic; pipes coordinate and transfer data. Systems have a unidirectional data flow; it can be monolithic or distributed.
  • When to use it: Suitable for systems with one-way, ordered processing steps. ETL pipelines, etc.

Microkernel

  • What is it: Topology consists of a core system (the "microkernel") and plug-ins. Plug-ins are optional and provide extensible functionality to the system. Traditionally monolithic with a single database. Plug-ins shouldn't access database directly.
  • When to use it: Installable desktop applications, or domains that address a wide market and require many custom rules and functionalities for each customer.

Service-Based

  • What is it: Distributed architecture with a separately deployed user interface, coarse-grained domain-centric remote services, and a monolithic database. Basically microservices but with coarser service boundaries and a single shared database, or just a few.
  • When to use it: When the system is of significant complexity and serves a wide enough user base that the benefits of a distributed architecture outweigh the costs. Can be a stepping stone towards other distributed architectures

Event-Driven

  • What is it: Distributed system using mostly asynchronous communication. Consists of event publishers, brokers, and processors (the services). Central communication unit is an event, as opposed to a request.
  • When to use it: Systems that require flexible, dynamic processing that need to scale to lots of concurrent users. Applications where eventual consistency is tolerable and immediate acknowledgement isn't needed.

Space-Based

  • What is it: A complicated distributed infrastructure of scalable processing units that are supported by replicated and/or distributed caches. There is a shared "data grid" that handles data syncing between units and reading/writing from the database. This removes the database bottleneck from the system—database access isn't needed for processing requests.
  • When to use it: Applications with very high concurrent user volume and high traffic variability, AND a low need for data consistency between users. Race conditions and data conflicts will be unavoidable in this system.

Orchestration-Driven Service-Oriented

  • What is it: A legacy architectural style that uses abstract service layers and operations orchestrated by a shared "enterprise service bus" which knows which services to call to complete operations. Uses generic components to increase code re-use.
  • When to use it: If you've taken a time machine back to the 90s and you have to write enterprise software.

Microservices

  • What is it: Domain-driven architecture that enforces strict API boundaries and minimizes coupling between domains. Duplication is favoured over re-use where possible. Each service should "do one thing" and have its own database ideally.
  • When to use it: Systems that are highly modular and have high enough load to justify the scalability and performance benefits compared to the development and operational costs.

stonehenge.png Pictured: Enterprise Service Java Beans from the Neolithic era. Thought to be a tribute to Sun Microsystems

What is a System?

It's important to understand how to define a system's boundaries. In the book, the authors define the concept of an architectural quantum which is the "smallest part of the system that runs independently". The system might be your entire microservice architecture, but if one part of it can function independently of other parts of the system, it forms its own architectural quantum.

So how does an architectural quantum run independently if it has to communicate with other parts of the system? The critical part is how the communication happens—whether it's synchronous or asynchronous:

The dependency turns them into a single architectural quantum. Asynchronous communication can help detangle architectural quanta because it removes that dynamic dependency

— Richards & Ford, Ch. 21, para. 48

If the operation of System A requires information from System B, then it's coupled to System B and they form a single architectural quantum. This means that System A's characteristics are impacted by System B's characteristics. If System A needs to be fast, we must ensure System B is fast, and consistently fast.

At my current company, every service is associated with a reliability tier. The service's tier determines many of its operational requirements. For instance, a tier 0 system (the highest tier) needs to be deployed in multiple regions for redundancy. It needs an on-call engineer, clearly defined SLAs, etc. But if a tier-0 system needs to retrieve data from a lower tier system as part of its operation, all of a sudden the lower tier system needs to be a tier-0 system. They become coupled.

In practice, there's some nuance here. Just because you call another service via HTTP and block the current process waiting for a response, doesn't mean the two services are fully coupled. As long as there's fallback functionality that doesn't constitute an error state, they needn't be considered coupled. If your service needs to be fast and the other service isn't reliably fast, you may implement a strict timeout and then fallback to some degraded functionality in the event the request times out. As an example, consider a new user recommendation system being built by your company's ML team. Your tier-0 homepage rendering service can still attempt to retrieve user recommendations from this new system, but as long as you can fallback to some other functionality (like just choosing the user's recently viewed content) we don't need to group that recommendation system in with our service and its strict functional requirements.

AI & Architecture

The 2nd edition of this book was published in April 2025. So of course, AI was brought up a lot. In general, the authors' stance was that AI is not an effective replacement for human architects—and they didn't seem optimistic that it could ever be.

Why? Because, as we’ve demonstrated in this book, everything in software architecture is a trade-off. LLMs are great for understanding knowledge, but to this day, they still lack the wisdom necessary to make appropriate decisions. That wisdom includes so much context that it’s much faster for the architect to solve a business problem by themselves than to teach an LLM all about the problem and its extended environment and context. The fact that we’ve included eight other intersections to be concerned about should be evidence enough that this is a daunting task.

— Richards & Ford, Ch. 33, para. 80

While I agree that the amount of context necessary to properly make architectural decisions is hard to shove into an LLM's context window right now, I don't believe that'll be the case for long. I have a feeling the opinions in this book will become outdated quite soon.

Also, despite the authors' insistence that "architecture is the stuff you can’t Google or ask an LLM about", I fully believe that AI tools are an indispensable tool for researching architectural decisions. They can explore the problem domain more completely and much faster than any human could. They can also illuminate trade-offs and nuances you might have missed. The fact that the authors' never mentioned this in their statements on AI utility is a major oversight. Every job function in software development, from junior dev to CTO, should be leveraging AI tooling at this point.

Overall

Like I mentioned at the start, I found FoSA to be a bit bloated. Also, the book didn't didn't really cover what I was looking for. I wanted a book that described more specific architectural patterns for solving common technical challenges like cache invalidation, database replication etc. Instead, it focuses exclusively on the overall system layout—how the domain boundaries are divided and what the physical topology looks like. And how to shake someone's hand properly.

I also think the book tried too hard to quantify complex system characteristics. I don't find much use in assigning a 1 to 5 star rating for the "maintainability" of a "microkernel" architecture style (which is 3/5 according to the book)—simply because both the characteristic and the style itself are too vaguely defined to warrant a rating. I'm certain you could build your microkernel system to have poor maintainability OR incredible maintainability. There's too much ambiguity to extract any conclusions from these assessments.

Still, in general, FoSA is an interesting book that tackles one of the more complex and less formally researched areas of software development. Architectural decisions are the hardest to make due to their consequences and trade-offs, so knowing the patterns that have worked for others is a great starting point.