# A system is a graph

> This section covers the model the rest of the page evaluates, in which a system is a graph and a question about the system is a question about reach, answered…

Page: Architecture · Model
Canonical: https://banes-lab.com/software-architecture#a-system-is-a-graph

This section is stop 52 of 102 in the learning route. Previous: [02 - Limits](https://banes-lab.com/pag/validation/limitations.md). Next: [02 - Definitions own what, code owns how](https://banes-lab.com/software-architecture/model/definitions-own-what.md). It builds on [31 - Derived state](https://banes-lab.com/disciplined-methodology/verify/derived-state.md).

This section covers the model the rest of the page evaluates, in which a system is a graph and a question about the system is a question about reach, answered by a walk over that graph. [A1·a the four parts](https://banes-lab.com/software-architecture#a-system-is-a-graph-panel-a) shows the parts of the model, [A1·b one feature](https://banes-lab.com/software-architecture#a-system-is-a-graph-panel-b) shows a walk over one feature, and [A1·c the four fractures](https://banes-lab.com/software-architecture#a-system-is-a-graph-panel-c) shows the test the model applies. The practice of the model for a tree is described in [derived state](https://banes-lab.com/disciplined-methodology/verify/derived-state.md) on the methodology page.

### Components, relations, schema, propagation

Software is usually reasoned about as a pile of parts rather than as a graph, so neither the developer nor the model can say what a change reaches or what the system claims about itself. A limit is declared in two places, a state is written by hand where nothing reads it back, a description goes stale beside the code it describes, and a step is performed manually. None of the four is recorded as a defect, because none of them has a name. A system described in prose has parts that cannot be enumerated and relations that cannot be traversed, so every question about it is answered by recollection.

For this reason a system is modelled as components, relations, an evaluative schema and a propagation topology, and it is healthy when its behaviour is derivable from its self-description. The graph is modelled before any part of it, rather than parts being assembled and the graph inferred afterwards. In practice, the components and the relation kinds are named, the schema is stated as invariants a walk can evaluate over the graph, and the way a change propagates is stated, so [impact analysis](https://banes-lab.com/records/arch/impact-analysis.md) is a traversal rather than a guess. Every value the system carries is then tested by asking whether the system can observe that value in its own self-description, and a value that fails the question is treated as a fracture.

To check this, pick a component and derive what a change to it reaches, from the graph alone. Then change it and observe what actually moved. The difference between the two sets is the part of the topology that was never modelled. The model is a description of structure, and it says nothing about which components are good. A graph with clean edges and a bad decomposition is still a graph, and the decomposition is judged by the schema applied to it, never by the model that holds it.

### Why a graph and not a list

Which modules a change touches is a walk over the [dependency graph](https://banes-lab.com/records/arch/dependency-graph.md). Which of two components knows about the other is the direction of one edge. Whether the whole can be built in one pass is whether the graph is a [directed acyclic graph](https://banes-lab.com/records/arch/directed-acyclic-graph.md), and a [circular dependency](https://banes-lab.com/records/arch/circular-dependency.md) is the one shape that makes the answer no.

[Traceability](https://banes-lab.com/records/arch/traceability.md), from a requirement to the code that carries it and back, is a path. [Modularity](https://banes-lab.com/records/arch/modularity.md), [loose coupling](https://banes-lab.com/records/arch/low-coupling.md) and [high cohesion](https://banes-lab.com/records/arch/high-cohesion.md) are each a statement about how many edges cross a boundary and how many stay inside it. A list can hold every one of those facts and can answer none of the questions, because a list has no edges to walk.

### The four fractures

[Self-describing architecture](https://banes-lab.com/records/arch/self-describing-architecture.md) is the property the health test names, and it is stronger than [observability](https://banes-lab.com/records/arch/observability.md). An observable system can be watched from outside. A self-describing one carries its own description as data it can read, and [introspection](https://banes-lab.com/records/arch/introspection.md) over that data answers the reach questions without running anything.

The four fractures are the four ways a description and a behaviour come apart. A [dual write](https://banes-lab.com/records/arch/dual-write.md) is one fact declared twice, and it disagrees with itself the moment either copy moves. A [hidden side effect](https://banes-lab.com/records/arch/hidden-side-effect.md) is a value the system changes where nothing reads it back, so it can be wrong forever. [Schema drift](https://banes-lab.com/records/arch/schema-drift.md) is a description the system cannot check against itself, so it is right only on the day it was written. [Manual-only governance](https://banes-lab.com/records/arch/manual-only-governance.md) is a step the description does not contain, so the system behaves differently depending on who performs it.

### The example is the system you have

Take any system you already have and ask of each fact where it is declared and what reads that declaration. A port written in a manifest and again in a start script is a dual write. A [feature toggle](https://banes-lab.com/records/arch/feature-toggle.md) flipped by hand in a console is a hidden side effect. A document that names three services where the tree holds four is schema drift. A release checklist a developer walks is manual-only governance.

None of the four is a bug in the ordinary sense. All four are fractures in the derivation, and each has the same repair, which is one declaration with every other appearance derived from it or deleted.

A1·a the four parts

```mermaid
flowchart TB
components["Components · the nodes"]
relations["Relations · the edges"]
schema["The evaluative schema · applied to the graph"]
propagation["The propagation topology · how a change travels"]
rest["The graph at rest · what the system is"]
motion["Propagation · the system in motion"]
healthy{"Is the behaviour derivable from the self-description?"}
yes["Healthy"]
fracture["A fracture · dual write, hidden side effect, schema drift, manual-only governance"]
components --> rest
relations --> rest
schema --> rest
propagation --> motion
rest --> healthy
motion --> healthy
healthy -- yes --> yes
healthy -- no --> fracture
```

A1·b one feature

```mermaid
flowchart LR
subgraph domain["domain"]
model["order model"]
policy["pricing policy"]
end
subgraph application["application"]
coordinator["checkout coordinator"]
store["cart store"]
end
subgraph processing["processing"]
converter["order converter"]
validator["order validator"]
end
subgraph product["product"]
view["checkout view"]
end
view --> coordinator
coordinator --> store
coordinator --> converter
converter --> model
validator --> model
validator --> policy
coordinator --> validator
policy -. circular dependency .-> coordinator
```

A1·c the four fractures

```mermaid
flowchart LR
test{"Can the system observe this value in its own self-description?"}
dual["Dual write · one fact declared twice"]
hidden["Hidden side effect · a value nothing reads back"]
drift["Schema drift · a description the system cannot check"]
manual["Manual-only governance · a step the description does not contain"]
closed["Closed · one declaration, derived everywhere"]
test -- no --> dual
test -- no --> hidden
test -- no --> drift
test -- no --> manual
test -- yes --> closed
```

## Links to

- [Derived state](https://banes-lab.com/disciplined-methodology/verify/derived-state.md)
- [Impact Analysis](https://banes-lab.com/records/arch/impact-analysis.md)
- [Dependency Graph](https://banes-lab.com/records/arch/dependency-graph.md)
- [Directed Acyclic Graph (DAG)](https://banes-lab.com/records/arch/directed-acyclic-graph.md)
- [Circular Dependency](https://banes-lab.com/records/arch/circular-dependency.md)
- [Traceability](https://banes-lab.com/records/arch/traceability.md)
- [Modularity](https://banes-lab.com/records/arch/modularity.md)
- [Low Coupling](https://banes-lab.com/records/arch/low-coupling.md)
- [High Cohesion](https://banes-lab.com/records/arch/high-cohesion.md)
- [Self-Describing Architecture](https://banes-lab.com/records/arch/self-describing-architecture.md)
- [Observability](https://banes-lab.com/records/arch/observability.md)
- [Introspection](https://banes-lab.com/records/arch/introspection.md)
- [Dual Write](https://banes-lab.com/records/arch/dual-write.md)
- [Hidden Side Effect](https://banes-lab.com/records/arch/hidden-side-effect.md)
- [Schema Drift](https://banes-lab.com/records/arch/schema-drift.md)
- [Manual-Only Governance](https://banes-lab.com/records/arch/manual-only-governance.md)
- [Feature Toggle](https://banes-lab.com/records/arch/feature-toggle.md)

## Linked from

- [The plan is a graph](https://banes-lab.com/disciplined-methodology/plan/the-flat-checklist.md)
- [Derived state](https://banes-lab.com/disciplined-methodology/verify/derived-state.md)
- [The honest gaps](https://banes-lab.com/software-architecture/coverage/the-honest-gaps.md)
- [The four parts, measured](https://banes-lab.com/anatomy/reading/the-four-parts-measured.md)
