Part I — Software Architecture Fundamentals · Chapter 5

Architecture Quanta, Metrics and Governance

~40 min read6 interactive widgets4 plates

In this chapter

  1. From components to architecture quanta
  2. Static and dynamic coupling
  3. Counting quanta
  4. Dynamic quantum coupling: the three forces
  5. Why measure at all
  6. Measuring cohesion: LCOM
  7. Measuring coupling: afferent, efferent, connascence
  8. Measuring complexity: cyclomatic complexity
  9. Governance and fitness functions
  10. Documenting decisions: ADRs
  11. Check your understanding

1. From components to architecture quanta

Components, as we saw in Chapter 4, are a design-time notion. The architecture quantum concept was introduced [SAH] to measure aspects of both topology and behaviour in software architecture, related to how parts connect and communicate with one another.

Definition

An architecture quantum is an independently deployable artefact with:

Each clause carries weight, and the module unpacks all three.

ClauseWhat it means
Independently deployableAn architecture quantum represents a deployable asset within the architecture: each quantum is a separate deployable unit. A monolithic architecture — one deployed as a single unit — is by definition a single architecture quantum.
High functional cohesionStructurally, the proximity of related elements: classes, components, services. From a purely independent-deployability standpoint a giant monolith qualifies as a quantum, but it almost certainly is not highly functionally cohesive — it includes the functionality of the entire system. The larger the monolith, the less likely it is singularly functionally cohesive.
High static couplingThe elements inside the quantum are tightly wired together — which is really an aspect of contracts.

Why the concept earns its keep: the architecture quantum boundary provides a useful common language among architects, developers and operations. Each understands the common scope under question — architects understand the coupling characteristics, developers understand the scope of behaviour, and the operations team understands the deployable characteristics. And the quantum represents one of the forces (static coupling) architects must consider when striving for proper granularity of services within a distributed architecture.

2. Static and dynamic coupling

The definition uses two different kinds of coupling in the same breath, and telling them apart is examinable.

Static couplingDynamic coupling
RepresentsHow static (operational) dependencies resolve within the architecture via contractsHow quanta communicate at runtime, i.e. communication dependencies, either synchronously or asynchronously
DescribesHow components are wired together — e.g. a service with its own databaseHow components call one another at runtime — e.g. a service calling another service
ConsequenceA component is not operational without the necessary data and componentsNeither component requires the other to be present to function, except for some workflow at runtime
For the exam

The one-line test: static coupling asks “what must be deployed for this thing to start at all?”; dynamic coupling asks “what must be up for this workflow to complete?” A service and its own database are statically coupled — the service is not operational without it. Two services calling each other are dynamically coupled — each can boot and live alone; only the shared workflow needs both.

3. Counting quanta

The module works through the cases, and the surprising ones are the most instructive.

The user interface trap

Here is the case that catches everybody:

Careful

If the system is tightly coupled to a user interface, the architecture forms a single architecture quantum — even if it is based on microservices. User interfaces create coupling points between the front end and the back end, and most user interfaces will not operate if portions of the backend are not available.

Architects can design user interfaces using asynchronicity so that no coupling is created between front and back — for example a microfrontend framework, where the user interface elements that interact on behalf of the services are emitted from the services themselves.

HOW MANY QUANTA? (a) monolith all modules database 1 quantum (b) distributed, ONE database svc A svc B svc C shared database still 1 quantum static coupling includes the DB (c) microservices svc A db svc B db svc C db 3 quanta each service its own characteristics (d) the same microservices, behind a tightly coupled user interface monolithic user interface svc A svc B svc C 1 quantum again the UI will not operate if part of the backend is unavailable
Plate 5.1 — Four topologies, three different quantum counts. Case (b) and case (d) are the ones that quietly undo a microservices migration.

The pay-off of multiple quanta is stated explicitly: each service, acting as a bounded context, may have its own set of architecture characteristics — one service might have higher levels of scalability than another. This granular level of architecture-characteristics scoping represents one of the advantages of the microservices architecture style: high degrees of decoupling allow teams working on a service to move as quickly as possible without worrying about breaking other dependencies.

4. Dynamic quantum coupling: the three forces

The last portion of the quantum definition concerns synchronous coupling at runtime — the behaviour of architecture quanta as they interact with one another to form workflows within a distributed architecture.

The nature of how components call one another creates difficult trade-off decisions, because it is a multidimensional decision space influenced by three interlocking forces.

ForceQuestion it answersPoles
CommunicationWhat type of connection synchronicity is used?synchronous ↔ asynchronous
ConsistencyDoes the workflow communication require atomicity, or can it use eventual consistency?atomic ↔ eventual
CoordinationDoes the workflow use an orchestrator, or do the services communicate via choreography?orchestration ↔ choreography

Communication

Synchronous communication requires the requestor to wait for the response from the receiver: the calling service makes a call and blocks — does no further processing — until the receiver returns a value, or a status indicating a state change or an error condition.

Asynchronous communication: the caller posts a message to the receiver, usually via a mechanism such as a message queue, and once the caller gets acknowledgement that the message will be processed, it returns to work. If the request requires a response value, the receiver can use a reply queue to asynchronously notify the caller of the result.

Consistency

Consistency refers to the strictness of transactional integrity that communication calls must adhere to. Atomic transactions — all-or-nothing, requiring consistency during the processing of a request — lie on one side of the spectrum; different degrees of eventual consistency lie on the other.

Careful

Transactionality — having several components participate in an all-or-nothing transaction — is one of the most difficult problems to model in distributed architectures. Hence the general advice: try to avoid cross-component transactions; keep transactions inside a component, not across. Chapter 7 turns this into the aggregate rule “one transaction creates or updates one aggregate”, and Chapter 9 turns it into the saga pattern.

Coordination

Coordination refers to how much coordination the workflow modelled by the communication requires. The two common generic patterns for distributed components are orchestration and choreography. Simple workflows — a single component replying to a request — do not require special consideration from this dimension; but as the complexity of the workflow grows, so does the need for coordination.

CONSISTENCY atomic eventual COMMUNICATION synchronous asynchronous COORDINATION orchestration choreography sync + atomic + orchestrated: transactionality is EASIER here async + eventual + choreographed: higher levels of SCALE are possible here Architects cannot make these choices in isolation: each option has a GRAVITATIONAL EFFECT on the others.
Plate 5.2 — The multidimensional decision space. For a particular decision an architect can graph the position in space representing the strength of these forces.

The module’s summary sentence is worth memorising: transactionality is easier in synchronous architectures with mediation, whereas higher levels of scale are possible with eventually consistent asynchronous choreographed systems.

5. Why measure at all

Measuring is essential for evaluating an architecture. The module distinguishes three kinds of measure:

KindMeasuresExamples
Operational measuresOperational characteristicsperformance, scalability
Structural measuresStructural characteristicsmodularity
Process measuresCharacteristics of the software development processdeployability, testability

The architect has two tools for this: Quality Attribute Scenarios (Chapter 2) and fitness functions (below).

The physics analogy for modularity

Modularity is an organising principle: paying attention to how the pieces wire together. The module offers a physics analogy that is worth quoting because it explains why governance exists at all: software systems, like complex systems in general, tend towards disorder — increasing entropy. Energy must be added to a physical system to preserve order; analogously, architects must constantly spend energy to ensure good structural soundness, which will not happen by accident.

Two definitions used from here on:

Given the importance of modularity, architects need tools to understand it with language-agnostic metrics: cohesion, coupling, connascence.

6. Measuring cohesion: LCOM

Cohesion: to what extent the parts of a module should be contained within the same module — how related the parts are to one another. Ideally, a cohesive module is one where all the parts should be packaged together, because breaking them into smaller pieces would require coupling the parts together via calls between modules to achieve useful results. Larry Constantine’s formulation: “attempting to divide a cohesive module would only result in increased coupling and decreased readability”.

Computer scientists have defined a range of cohesion measures: functional, sequential, communicational, procedural, temporal and logical cohesion.

The metric the module names is the Chidamber and Kemerer Lack of Cohesion in Methods (LCOM) metric, which measures the structural cohesion of a module — typically a component. Applied to object-oriented programming: the sum of sets of methods not shared via sharing fields.

The reading of the classic example is:

Editor’s note

Note the direction of the scale: LCOM measures the lack of cohesion, so low is good. It is an easy thing to invert under exam pressure.

7. Measuring coupling: afferent, efferent, connascence

Coupling in code bases is analysed by exploiting graph theory: the method calls and returns form a call graph. In 1979 Edward Yourdon and Larry Constantine published Structured Design, defining many core concepts including two coupling metrics:

MetricDefinition
Afferent couplingMeasuring the number of incoming connections to a code artefact (component, class, function, and so on)
Efferent couplingMeasuring the outgoing connections to other code artefacts

For virtually every platform, tools exist that let architects analyse the coupling characteristics of code in order to assist in restructuring, migrating or understanding a code base.

Connascence

Connascence was introduced in 1996 by Meilir Page-Jones in What Every Programmer Should Know About Object-Oriented Design, refining the afferent and efferent coupling metrics and recasting them for object-oriented languages.

Definition

Two components are connascent if a change in one would require the other to be modified in order to maintain the overall correctness of the system.

TypeConcernsNamed forms
Static connascenceStatic aspects of codeConnascence of Name (CoN), Connascence of Type (CoT), Connascence of Position (CoP), Connascence of Algorithm (CoA)…
Dynamic connascenceCalls at runtimeConnascence of Execution (CoE), Connascence of Timing (CoT), Connascence of Identity (CoI)

Strength of connascence

Architects determine the strength of connascence by the ease with which a developer can refactor that type of coupling. Different types of connascence are demonstrably more desirable than others, and architects and developers can improve the coupling characteristics of their code base by refactoring toward better types of connascence.

The three guidelines for using connascence to improve a system’s modularity:

  1. Minimise overall connascence by breaking the system into encapsulated elements.
  2. Minimise any remaining connascence that crosses encapsulation boundaries.
  3. Maximise the connascence within encapsulation boundaries.
STATIC — about the code as written CoN — of Name CoT — of Type CoP — of Position CoA — of Algorithm DYNAMIC — about what happens at runtime CoE — of Execution CoT — of Timing CoI — of Identity Strength is judged by how easily a developer can refactor that kind of coupling away. THE THREE GUIDELINES INSIDE a boundary MAXIMISE connascence things that change together belong together — this is cohesion, restated ACROSS a boundary MINIMISE connascence every remaining cross-boundary connascence is a pair of things that must be changed in lockstep by two different teams — Chapter 10 calls this a contract
Plate 5.3 — Connascence in one view. The guidelines are the precise, measurable version of “high cohesion, low coupling” from Chapter 1.

8. Measuring complexity: cyclomatic complexity

Cyclomatic complexity (CC) is a code-level metric designed to provide an objective measure of the complexity of code, at the function/method, class or application level. It is computed by applying graph theory to code, specifically to decision points, which cause different execution paths.

If a function has no decision statements — no if statements — then CC = 1. If the function has a single conditional, then CC = 2, because two possible execution paths exist.

CC = E − N + 2

  N = nodes (lines of code)
  E = edges (possible decisions)

  worked example from the module:  CC = 3   (3 − 2 + 2)

Guidelines

9. Governance and fitness functions

The module first states the limit of the classical metrics: traditional OOP-oriented metrics alone lack evaluation of issues that are important in modern software systems, such as concurrency, interaction and distribution — for example whether an API is synchronous or asynchronous. Modern systems call for a more comprehensive approach, based on the identification of a wider spectrum of quality attributes.

Which raises the question: once quality attributes have been established and prioritised, how can the architect make sure that these qualities and priorities will be respected?

Architecture governance

The word derives from the Greek kubernan, “to steer”. Governance is an important responsibility of the architect role: ensuring software quality within an organisation falls within the scope of architecture, and negligence can lead to disastrous quality problems.

Fitness functions

The objective is automating aspects of architecture governance, in spite of the continuous change, adaptation and evolution of software systems and requirements.

An architecture fitness function is any mechanism that provides an objective integrity assessment of some architecture characteristic or combination of architecture characteristics — assessing how close the output comes to achieving the aim.

Crucially, fitness functions are not some new framework for architects to download, but rather a new perspective on many existing tools. They overlap many existing verification mechanisms, depending on the way they are used: as metrics, monitors, unit testing libraries, chaos engineering, and so on.

ToolFitness function example
JDependDetecting component cycles (cyclic dependencies) — cyclic dependencies are a main architectural anti-pattern that leads towards big balls of mud
ArchUnitInspired by and using the JUnit ecosystem, focusing on architectural aspects (archunit.org). Example: enforcing layering

10. Documenting decisions: ADRs

Making architectural decisions is a core expectation of an architect: decisions involving the structure of the application or system, but also technology decisions when these impact architecture characteristics. Michael Nygard’s term for the ones that count is architecturally significant decisions: those decisions that affect the structure, non-functional characteristics, dependencies, interfaces or construction techniques.

The key need is documenting them — to track motivations, to avoid steps back. The approach: Architecture Decision Records (ADRs), short text files, usually one to two pages long, describing a specific architecture decision. First evangelised by M. Nygard, now widely adopted.

The sections

Usually numbered sequentially, containing a short phrase describing the architecture decision. It should be descriptive enough to remove any ambiguity about the nature and context of the decision, but at the same time short and concise.

42. Use of Asynchronous Messaging Between Order and Payment Services.

Can be marked as Proposed, Accepted or Superseded.

  • Proposed — the decision must be approved by either a higher-level decision maker or an architectural governance body, such as an architecture review board.
  • Accepted — approved and ready for implementation.
  • Superseded — changed and superseded by another ADR.
ADR 42. Use of Asynchronous Messaging Between Order and Payment Services
Status: Superseded by 68

ADR 68. Use of REST Between Order and Payment Services

The superseded status always assumes the prior ADR status was accepted: a proposed ADR would never be superseded, but rather continue to be modified until accepted. This is a powerful way of keeping a historical record of what decisions were made, why they were made at that time, what the new decision is and why it changed.

A further status is Request for Comments (RFC), used when the architect wants to validate assumptions and assertions with a larger audience of stakeholders, specifying a deadline:

Status: Request For Comments, Deadline 09 JAN 2026

Specifying the forces at play: what situation is forcing me to make this decision? It allows the architect to describe the specific situation or issue and concisely elaborate on the possible alternatives.

The order service must pass information to the payment service to
pay for an order currently being placed. This could be done using
REST or asynchronous messaging.

Note the side benefit: by describing the context, the architect is also describing the architecture — an effective way of documenting a specific area of the architecture in a clear and concise manner.

Contains the architecture decision, along with a full justification, in an affirmative style.

Decision: we will use asynchronous messaging between services,
to improve uncoupling

This section places emphasis on the why rather than the how. Knowing why a decision was made, and its justification, helps people better understand the context of the problem and avoids possible mistakes through refactoring to another solution that might produce issues.

Documenting the overall impact of an architecture decision, both good and bad, and then the trade-off analysis associated with the decision.

This is the section that makes an ADR different from a wiki page: it forces the author to write down what the decision costs, not only what it buys.

A non-standard section from [FSA]. The Compliance section forces the architect to think about how the architecture decision will be measured and governed from a compliance perspective: the architect must decide whether the compliance check for this decision must be manual, or whether it can be automated using a fitness function.

All shared objects used by business objects in the business layer will
reside in the shared services layer to isolate and contain shared
functionality.

If it can be automated, the architect specifies here how that fitness function would be written, and whether any other changes to the code base are needed to measure this decision for compliance.

Metadata about the ADR itself: original author, approval date, approved by, superseded date, last modified date, modified by, last modification.

QUALITY ATTRIBUTES established & prioritised DECISIONS (ADRs) context / decision / consequences FITNESS FUNCTIONS objective integrity assessment CI PIPELINE runs on every build a failing fitness function is feedback, not a build error to silence ENTROPY ARGUMENT (from the module): software systems, like complex systems, tend towards disorder — increasing entropy. energy must be added to a physical system to preserve order; analogously architects must spend energy constantly to ensure structural soundness. A fitness function is that energy, automated. Governance = kubernan = to steer.
Plate 5.4 — The governance loop. The Compliance section of an ADR is precisely the bridge from the second box to the third.

Check your understanding

Give the full definition of an architecture quantum.

An independently deployable artefact with high functional cohesion, high static coupling and synchronous dynamic coupling. It was introduced to measure aspects of both topology and behaviour, related to how parts connect and communicate with one another.

Distinguish static from dynamic coupling with an example of each.

Static coupling represents how static (operational) dependencies resolve within the architecture via contracts — it describes how components are wired together, e.g. a service with its own database: the component is not operational without the necessary data and components. Dynamic coupling represents how quanta communicate at runtime, synchronously or asynchronously — e.g. a service calling another service: neither component requires the other to be present to function, except for some workflow at runtime.

A distributed system with six services all sharing one database: how many quanta, and why?

One. Any architecture that deploys using a single database always has a single quantum, because the architecture quantum measure of static coupling includes the database, and a system relying on a single database cannot have more than a single quantum. The module names two such cases explicitly: the device-based architecture style, and an event-driven architecture with a centralised event broker.

Why can a microservices system still be a single quantum?

Because of the user interface. If the system is tightly coupled to a UI, the architecture forms a single architecture quantum even if it is based on microservices: user interfaces create coupling points between front end and back end, and most UIs will not operate if portions of the backend are unavailable. The remedy is to design the UI using asynchronicity — for example a microfrontend framework, where the UI elements that act on behalf of the services are emitted from the services themselves.

What is the practical benefit of having many quanta rather than one?

Each service, acting as a bounded context, may have its own set of architecture characteristics — one service might need higher scalability than another. This granular scoping of architecture characteristics is one of the advantages of microservices, and the high degree of decoupling allows the team on a service to move as fast as possible without worrying about breaking other dependencies.

Name the three forces of dynamic quantum coupling and their poles.

Communication (synchronous ↔ asynchronous), consistency (atomicity ↔ eventual consistency) and coordination (orchestration ↔ choreography). They form a multidimensional decision space, and architects cannot choose in isolation: each option has a gravitational effect on the others. Transactionality is easier in synchronous architectures with mediation, whereas higher levels of scale are possible with eventually consistent asynchronous choreographed systems.

What general advice does the course give about transactions across components?

That transactionality — several components participating in an all-or-nothing transaction — is one of the most difficult problems to model in distributed architectures, hence the general advice to avoid cross-component transactions: keep transactions inside a component, not across. This is the same rule that reappears as the aggregate transaction boundary in Chapter 7 and as the saga pattern in Chapter 9.

What does LCOM measure, and in which direction is it good?

The Chidamber and Kemerer Lack of Cohesion in Methods metric measures the structural cohesion of a module, typically a component; applied to OOP it is the sum of sets of methods not shared via sharing fields. Since it measures the lack of cohesion, a low score is good. A class with a high LCOM has field/method pairs that could each move into their own class without affecting behaviour.

Define connascence and its two families, and give the three guidelines.

Two components are connascent if a change in one would require the other to be modified in order to maintain the overall correctness of the system. Static connascence concerns static aspects of code (Name, Type, Position, Algorithm); dynamic connascence concerns calls at runtime (Execution, Timing, Identity). Guidelines: (1) minimise overall connascence by breaking the system into encapsulated elements; (2) minimise any remaining connascence that crosses encapsulation boundaries; (3) maximise the connascence within encapsulation boundaries.

How is the strength of connascence judged, and what do you do with the judgement?

By the ease with which a developer can refactor that type of coupling: some types of connascence are demonstrably more desirable than others. Architects and developers improve their code base by refactoring toward better types of connascence — i.e. converting a hard-to-refactor coupling into an easier one, rather than trying to remove all coupling.

Give the cyclomatic complexity formula and the thresholds.

CC = E − N + 2, where N represents nodes (lines of code) and E represents edges (possible decisions). A function with no decision statements has CC = 1; with one conditional, CC = 2. Industry thresholds: CC ≤ 10 acceptable, barring considerations such as complex domains; [FSA] suggests CC < 5 for cohesive, well-factored code; and Crap4J’s dictum is that above 50 no amount of code coverage rescues that code from crappiness.

Define a fitness function, and say what it is not.

Any mechanism that provides an objective integrity assessment of some architecture characteristic or combination of architecture characteristics — assessing how close the output comes to achieving the aim. It is not a new framework for architects to download: it is a new perspective on many existing tools, overlapping existing verification mechanisms depending on how they are used — metrics, monitors, unit testing libraries, chaos engineering. Named examples: JDepend for detecting cyclic component dependencies, ArchUnit for layering rules.

List the standard ADR sections and the two extensions, and explain the Superseded status.

Standard: Title, Status, Context, Decision, Consequences. Extensions from [FSA]: Compliance and Notes. Superseded always assumes the prior ADR was accepted — a proposed ADR would never be superseded, only modified until accepted. Superseding keeps a historical record of what was decided, when, why, and why it changed. A further status is Request for Comments, with a deadline.

Why is the Compliance section the interesting one?

Because it forces the architect to think about how the decision will be measured and governed: the architect must decide whether the compliance check must be manual, or whether it can be automated using a fitness function — and if automated, specify how that fitness function would be written and what changes to the code base are needed to measure the decision. It is the bridge from a written decision to enforced governance.