FE/26
MENU

2026-07-28 · 11 min read

Risk, Complexity, and Pressure

ArchitectureSoftware DesignRiskComplexity

After publishing Architecture Must Follow Pressure, I started wondering whether I had accidentally given a new name to an old idea. This is always a dangerous question. You spend years watching systems fail, extract a principle from the wreckage, give it a memorable name, and then discover that someone described most of it more rigorously fifteen years ago.

In this case, I found two close relatives:

Neither says exactly that architecture must follow pressure. But together they clarify what I was trying to express. Fairbanks asks where architectural effort is justified. Ousterhout asks whether a design actually hides complexity. Pressure adds a third question:

What force made this mechanism necessary?

Preparing for the wrong disaster

There is a recurring scene in architecture reviews. Someone introduces an interface in front of a database repository.

“Why do we need this interface?”

“We may replace the database one day.”

“Is that planned?”

“No.”

“Is there another implementation?”

“No, but there could be.”

The interface is approved. It is architecture, after all. Soon there is an interface, an implementation, an adapter, a factory, and a configuration class.

Six months later, the application fails because an external service starts taking eight seconds to respond. There is no aggressive timeout, no concurrency isolation, no graceful degradation. The database remains exactly where it was. The hypothetical replacement was beautifully abstracted; the actual failure was allowed to propagate through the system.

The adapter survived.

The service did not.

This is the gap I was trying to describe with the word pressure. Architecture should not be organised around every event that can be imagined. It should respond to forces sufficiently real, costly, or dangerous that the simpler design can no longer tolerate them.

Fairbanks: risk must pay for architecture

In Just Enough Software Architecture, George Fairbanks argues against applying the same architectural process and level of rigour to every system. His model is simple: identify and prioritise risks, apply techniques that reduce them, then check whether they were actually reduced.

“There is no need for meticulous designs when risks are small, nor any excuse for sloppy designs when risks threaten your success.”

The important point is not merely that architects should think about risk. Risk should determine where architectural effort is spent and how much of it is justified — because architecture has a cost. Abstractions cost comprehension. Boundaries cost coordination. Distributed components add latency, infrastructure, observability, deployment machinery, testing, and new failure modes. Those costs may be justified, but something must justify them.

This is close to the principle from my original article:

No architectural pressure, no architectural pattern.

Neither idea is anti-architecture. Both reject architecture by ceremony. The intervention should follow the threat.

From pressure to mechanism

Fairbanks uses risk to direct architectural attention. I use pressure to describe the force that makes a simpler design insufficient — and the two chain together.

DERIVATION PRESSURE RISK CONTROL MECHANISM the force what it costs the response what you build read it backwards to audit · what force is this resisting?
Forwards, it derives a mechanism. Backwards, it interrogates one — which is the direction most existing systems need.

Suppose an external dependency occasionally becomes slow. The slowness is the pressure. Resource exhaustion, cascading latency, and unavailability are the risks. Timeouts, concurrency limits, caching, or asynchronous processing are possible controls. Those controls create architecture, and now the mechanism has a reason to exist.

A queue does not exist because queues decouple things. It exists because the producer and consumer must not share availability or throughput. An idempotency key is not generic defensive programming; it exists because a timeout can make the result ambiguous, and a retry may duplicate an irreversible operation. A state machine is not an impressive diagram; it exists because partial progress must survive restarts and delayed messages.

This is where Fairbanks and pressure are closest. Both demand traceability between architecture and something that threatens success.

Risk explains priority; pressure explains shape

I would not use risk and pressure interchangeably. Fairbanks provides a method for deciding what deserves architectural attention. Pressure is more concerned with the shape of the response. I want to walk through a system and ask: what force is this resisting? Why is there a queue here? Why is this component isolated? Why is this workflow represented as durable state? Why is there a separate read model?

Risk tells us that something deserves attention. Pressure explains why the response has this particular form. It may come from production behaviour, supplier limitations, regulation, latency budgets, security constraints, organisational boundaries, or incidents that reveal invalid assumptions. These can all be translated into risks — but pressure keeps attention on the force itself and on how it deforms the system.

Risk helps us prioritise.

Pressure helps us explain shape.

Ousterhout: structure must earn its cost

The second close relative comes from John Ousterhout's A Philosophy of Software Design. Ousterhout treats complexity as the central problem in software design: anything about the structure of a system that makes it difficult to understand or modify. He identifies two major causes, dependencies, where one part cannot be understood or changed without considering others, and obscurity, where important information is not obvious. These appear as familiar symptoms — small changes require modifications in many places, developers must hold too much information in their heads, it is unclear which code must change.

You receive a harmless-looking ticket:

Add one field to the response.

Two days later, you have changed three domain models, four transfer objects, a mapper, a persistence entity, an event schema, and several fixtures. The field is still a string. The structure has amplified the change.

Ousterhout's answer is not more layers. It is deep modules.

Deep modules and shallow architecture

A deep module exposes a simple interface while hiding substantial complexity. Ousterhout's formulation is that the best modules are those whose interfaces are much simpler than their implementations. A filesystem is the classic example: its interface is small — open, read, write, close — while behind it may exist caching, allocation, permissions, buffering, concurrency, devices, and crash recovery. The caller receives significant capability without understanding most of the machinery. That is depth.

DEEP SHALLOW interface interface caching · allocation permissions · buffering concurrency · devices crash recovery userName != null && !userName.isBlank() hides far more than it charges charges the same rent
The ratio is the whole design. An interface is a fixed tax; only the volume beneath it decides whether the tax was worth paying.

A shallow module does the opposite. It introduces a new interface, file, type, and navigation step while hiding almost nothing.

java
final class UserNameValidator {
    boolean validate(String userName) {
        return userName != null && !userName.isBlank();
    }
}

This abstraction has not removed complexity. It has charged an interface tax for three boolean expressions. Which connects directly to a sentence from my original article:

The best modules hide meaningful complexity. The worst abstractions merely relocate it.

Every interface introduces complexity. A useful abstraction must hide substantially more than it creates. It has to earn its rent.

A boundary must contain something

A shallow architecture is like a house where every room has been divided into smaller rooms. Each room has exactly one responsibility. There is a room for sitting. A room for standing. A room containing the handle used to open the next room. Technically, the separation is impeccable. Unfortunately, reaching the kitchen requires passing through fourteen doors.

ARCHITECTURAL INTERIOR DESIGN kitchen 14 boundaries · 14 crossings · 0 contained decisions local simplicity, global complexity
Each separation is defensible on its own. The cost is only visible when you try to get somewhere.

Many codebases suffer from this kind of architectural interior design. Each class is small. Each layer is pure. The complete behaviour is distributed across so many units that nobody can see it without opening half the project. Local simplicity has created global complexity.

A boundary is useful when it contains something:

A boundary containing nothing is just a door in the middle of a corridor.

Complexity can be pressure

This is where Ousterhout and pressure strongly overlap, because complexity can itself make the simpler design insufficient. When every workflow duplicates the same retry and concurrency rules, those rules create pressure for a shared abstraction. When every caller must understand a supplier's strange error model, that complexity creates pressure for a boundary. When idempotency, timeouts, and state transitions are repeated across multiple components, the distributed knowledge creates pressure for a deeper module.

A useful abstraction appears because complexity has accumulated in a form that can be compressed. This is different from adding an interface because one day there might be two implementations. The first responds to observed complexity; the second responds to imagination.

Imagination has an unlimited budget.

Code does not.

Complexity and resilience are different objectives

Ousterhout's main concern is complexity experienced by developers. Pressure also includes forces acting on the running system and the organisation around it. A component may be easy to understand and still require isolation because it fails frequently. A module may have an elegant interface and still require separation because two teams deploy independently. A security boundary may increase local complexity but remain necessary because trust cannot cross it.

Ousterhout asks whether a design reduces complexity. Pressure asks whether it survives the forces acting upon it. Usually these goals reinforce each other. Sometimes they conflict: a distributed system is more complex than a monolith, yet independent scaling, regulatory separation, team ownership, or failure isolation may justify that cost.

Pressure does not seek the least complex architecture in absolute terms. It seeks the least complex architecture capable of withstanding the actual forces.

Fairbanks tells us when; Ousterhout tells us whether

Fairbanks asks whether a risk is significant enough to justify architectural effort. Ousterhout asks whether the resulting structure reduces complexity or merely creates more interfaces and dependencies. Pressure identifies the force that put the question on the table in the first place.

A worked example: remote vehicle operation

A client sends a command to unlock a door. The request times out and the client closes the connection. From the client's side that is the entire event.

It is not the entire event. The provider had already accepted the command and answered 202, and that answer arrived on a socket nobody was holding. Dispatch, execution, and the vehicle's acknowledgement all happen afterwards, to an audience of nobody.

the client is in the dark CLIENT PROVIDER VEHICLE unlock timeout · connection closed 202 unlocked poll / webhook
Closing the connection ends the client's knowledge, not the operation. Everything inside the shaded span happens anyway, and only the poll or webhook — a second, separate conversation — ever closes it.

Pressure. The client's knowledge and the system's state stop being the same thing the instant a connection ends, and nothing about that looks like a failure from either side.

Risk. Duplicate execution, incorrect user feedback, and divergence between the command state and the observed vehicle state.

Control. A durable operation identifier, idempotency, explicit command states, and reconciliation.

Mechanism. A durable command state machine with idempotent submission, and a second channel to read the result back.

Fairbanks tells us that the consequences make this worthy of architectural attention. Ousterhout tells us not to expose every retry rule, supplier state, and network oddity to every caller — that complexity belongs behind a deep capability. Pressure explains why that capability exists.

The architecture is no longer selected from a catalogue. It is derived.

When pressure disappears

A mechanism that once reduced risk can later become the main source of complexity. A supplier becomes reliable, but the translation layer still contains six internal models. Two teams merge, but their systems still communicate through a queue and reconcile eventual consistency. A second implementation is deleted, but every operation still crosses an interface designed for interchangeable providers.

THEN NOW pressure MECHANISM gone MECHANISM load justifies structure structure justifies itself the only thing that changed is the thing nobody was tracking
Mechanisms are load-bearing until they are not. Nothing in the code records the difference, which is why the check has to be deliberate.

The abstraction may have had a legitimate birth. That does not grant it immortality. Fairbanks asks whether the original risk still exists. Ousterhout asks whether the mechanism now costs more understanding than it saves. Pressure asks:

Is the force this structure was built to resist still present?

When the answer is no, deleting architecture may be the most architectural decision available.

Putting the three ideas together

Fairbanks approaches architecture through engineering risk. Ousterhout approaches design through complexity. I arrived at pressure through production systems, failure analysis, and watching useful patterns become harmful when separated from the conditions that justified them.

The three ideas fit together:

Use risk to decide what deserves architectural attention. Use complexity to judge whether the design is helping. Use pressure to explain why the system must have that shape.

The pattern still comes last.

First, find the force.

References

  1. George Fairbanks, Just Enough Software Architecture: A Risk-Driven Approach. Marshall & Brainerd, 2010. https://www.georgefairbanks.com/book/

  2. John Ousterhout, A Philosophy of Software Design, Second Edition. Yaknyam Press, 2021. https://web.stanford.edu/~ouster/cgi-bin/book.php

  3. Fabio Ellena, “Architecture Must Follow Pressure,” 2026. https://fblln.github.io/articles/architecture-must-follow-pressure/