# Principles are typed

> This section covers how a principle is held as a typed record rather than a slogan.

Page: Architecture · Principles
Canonical: https://banes-lab.com/software-architecture/principles#principles-are-typed

This section is stop 56 of 102 in the learning route. Previous: [04 - The direction axis](https://banes-lab.com/software-architecture/model/the-direction-axis.md). Next: [02 - Every record has a kind](https://banes-lab.com/software-architecture/principles/every-record-has-a-kind.md). It builds on [01 - A system is a graph](https://banes-lab.com/software-architecture/model/a-system-is-a-graph.md), [02 - Definitions own what, code owns how](https://banes-lab.com/software-architecture/model/definitions-own-what.md).

This section covers how a principle is held as a typed record rather than a slogan. The record is typed in [A1·c a principle record](https://banes-lab.com/software-architecture/principles#principles-are-typed-panel-c), its slots are shown in [A1·a ten slots](https://banes-lab.com/software-architecture/principles#principles-are-typed-panel-a), and its edges are traced in [A1·b the five edges](https://banes-lab.com/software-architecture/principles#principles-are-typed-panel-b). The whole canon resolves by identity join, so a list of principles becomes something a check can consult, and [every record](https://banes-lab.com/ontology) this page names can be opened and walked on the ontology page.

### Ten slots, one gate

Architecture principles are usually a reading list, and a reading list cannot tell you which of its entries a given change just broke. A review cites a principle by name, a second review cites its opposite by another name, both are in the document, and neither review can show that the two were ever meant to conflict or how the conflict resolves. Prose principles cannot be joined, so nothing can compute which principle a finding violates, which repair follows, or which two principles a design has set against each other.

For this reason principles are typed and related, and a canon that does not resolve by identity is refused by its gate. The canon is held as data a gate resolves rather than as a document a reviewer cites. In practice, every principle has one identity that nothing else carries, a kind from a closed taxonomy and a category from the data that holds it. Its relations to the other records are recorded as names that resolve to identities, never as free prose, together with what violates it, what detects and measures a violation, the repairs that reverse it and the gates that enforce it. An exemplar with a before and an after is attached, and the gate proves the whole before anything consults it.

To check this, take a finding from any check and resolve its canonical id. It must reach the principle, the principle's severity, its repairs and its relation graph in one lookup. A finding that reaches only a message has no canon behind it. A canon is a vocabulary for reasoning, and it is the wrong home for a threshold or a path. A principle says that complexity is bounded. The number lives in the gate's config and the check reads it there. A canon that carries numbers is a second config, and a canon that carries paths is bound to one tree.

### The five edges

Requires says a principle cannot hold without another, so a design that adopts [modularity](https://banes-lab.com/records/arch/modularity.md) has adopted its [whole closure](https://banes-lab.com/records/algo/dependency-closure.md) whether it meant to or not. Reinforces says two principles hold more easily together, which is how an improvement [propagates](https://banes-lab.com/records/algo/reinforcement-propagation.md) past the record it was made to. Enables says one makes another possible.

In tension with says two records pull against each other on one construct, and every such pair carries a resolution whose mechanism follows from what the two records are. Conflicts with is the one negative edge, pointing from a principle to the [anti-patterns](https://banes-lab.com/records/kind/anti-pattern.md) that negate it, and the polarity law says it may [point nowhere else](https://banes-lab.com/ontology/schema/the-relation-ranges.md).

### The five descriptors

The descriptors are prose a developer reads. Violated-by describes the violation, detected-by the [signals that see it](https://banes-lab.com/records/algo/violation-detection.md), measured-by the numbers that size it, refactored-by the [repairs that reverse it](https://banes-lab.com/records/algo/refactor-selection.md), and enforced-by the [gates that hold the line](https://banes-lab.com/records/algo/enforcement-gate.md).

[Severity](https://banes-lab.com/records/algo/severity-policy.md) says how strictly the principle binds and, where the record states one, the condition under which it binds that strictly. It is a routing field that decides which handler a failure reaches, whether a refusal, a review note or an information line, and it never ranks one principle above another. A check still answers pass or fail, and severity says what happens to the answer.

### One record, walked

Modularity is a mandatory principle in the [structural core](https://banes-lab.com/records/layer/structural-core.md). It requires [high cohesion](https://banes-lab.com/records/arch/high-cohesion.md), [low coupling](https://banes-lab.com/records/arch/low-coupling.md) and [explicit boundaries](https://banes-lab.com/records/arch/explicit-boundaries.md), because a module whose insides do not belong together, or whose edges were never drawn, is not one you can replace. It reinforces [separation of concerns](https://banes-lab.com/records/arch/separation-of-concerns.md) and [composability](https://banes-lab.com/records/arch/composability.md), and it enables [replaceability](https://banes-lab.com/records/arch/replaceability.md) and [plugin architecture](https://banes-lab.com/records/arch/plugin-architecture.md).

It is in tension with cross-cutting concerns, and that pair carries a recorded resolution. It conflicts with the [big ball of mud](https://banes-lab.com/records/arch/big-ball-of-mud.md), the one anti-pattern that is its outright negation. A reading list can tell you modularity is good. The record can tell you what you have already committed to by choosing it, what you will get for free, and what it refuses.

Its descriptors meet the tree. It is violated by cyclic dependencies, [shared mutable state](https://banes-lab.com/records/arch/shared-mutable-state.md) and [boundary leakage](https://banes-lab.com/records/arch/boundary-leakage.md). It is detected by dependency cycles and an unstable module graph, measured by a modularity score, graph density and instability, and refactored by splitting the module, introducing a boundary or inverting the dependency. It is enforced by module rules, package ownership and [fitness functions](https://banes-lab.com/records/arch/fitness-functions.md). The last slot is what separates a canon from a book, because a principle with an empty enforced-by is held by nothing.

A1·a ten slots

```mermaid
flowchart TB
principle["A principle · one canonical id, one kind, one category, a severity"]
requires["requires · cannot hold without"]
reinforces["reinforces · holds more easily beside"]
enables["enables · makes possible"]
tensions["in tension with · a record it pulls against, resolved by mechanism"]
conflicts["conflicts with · the anti-patterns that negate it"]
violated["violated by · what a violation looks like"]
detected["detected by · the signals that see it"]
measured["measured by · the numbers that size it"]
refactored["refactored by · the repairs that reverse it"]
enforced["enforced by · the gates that hold it"]
principle --> requires
principle --> reinforces
principle --> enables
principle --> tensions
principle --> conflicts
principle --> violated
principle --> detected
principle --> measured
principle --> refactored
principle --> enforced
gate["A resolution gate · every edge resolves to a record or a defined term, every kind is in range, or the gate refuses"]
requires -.-> gate
tensions -.-> gate
conflicts -.-> gate
```

A1·b the five edges

```mermaid
classDiagram
class Principle {
id
name
kind
category
severity
violatedBy
detectedBy
measuredBy
refactoredBy
enforcedBy
}
class Term {
id
name
kind
definition
}
class AntiPattern {
id
absentControl
}
class Layer {
id
observes
feeds
}
Principle --> Principle : requires
Principle --> Principle : reinforces
Principle --> Principle : enables
Principle --> Term : tensions with
Principle --> AntiPattern : conflicts with
Principle --> Layer : member of
```

A1·c a principle record

```typescript
export type Kind =
| "anti-pattern" | "metric" | "quality-attribute" | "principle" | "constraint"
| "capability" | "activity" | "pattern" | "mechanism" | "technique"
| "approach" | "model" | "artifact" | "style";

export interface Principle {
readonly id: string;
readonly name: string;
readonly type: Kind;
readonly category: string;
readonly scope: readonly string[];
readonly requires: readonly string[];
readonly reinforces: readonly string[];
readonly enables: readonly string[];
readonly conflictsWith: readonly string[];
readonly tensionsWith: readonly string[];
readonly violatedBy: string;
readonly detectedBy: string;
readonly measuredBy: string;
readonly refactoredBy: string;
readonly enforcedBy: string;
readonly severity: string;
readonly exemplar?: { readonly before: string; readonly after: string; readonly lang: string };
}

export interface Issues {
readonly danglingEdges: readonly { from: string; relation: string; target: string }[];
readonly duplicateIds: readonly string[];
}

export const validate = (canon: readonly Principle[], resolveId: (name: string) => string | null): Issues => {
const ids = new Set(canon.map((principle) => principle.id));
const edges = ["requires", "reinforces", "enables", "conflictsWith", "tensionsWith"] as const;
return {
danglingEdges: canon.flatMap((principle) =>
edges.flatMap((relation) =>
principle[relation]
.filter((target) => resolveId(target) === null && !ids.has(target))
.map((target) => ({ from: principle.id, relation, target })),
),
),
duplicateIds: canon.map((principle) => principle.id).filter((id, index, all) => all.indexOf(id) !== index),
};
};
```

## Links to

- [Modularity](https://banes-lab.com/records/arch/modularity.md)
- [Dependency Closure](https://banes-lab.com/records/algo/dependency-closure.md)
- [Reinforcement Propagation](https://banes-lab.com/records/algo/reinforcement-propagation.md)
- [anti-pattern](https://banes-lab.com/records/kind/anti-pattern.md)
- [The relation ranges](https://banes-lab.com/ontology/schema/the-relation-ranges.md)
- [Violation Detection](https://banes-lab.com/records/algo/violation-detection.md)
- [Refactor Selection](https://banes-lab.com/records/algo/refactor-selection.md)
- [Enforcement Gate](https://banes-lab.com/records/algo/enforcement-gate.md)
- [Severity Policy](https://banes-lab.com/records/algo/severity-policy.md)
- [Structural Core](https://banes-lab.com/records/layer/structural-core.md)
- [High Cohesion](https://banes-lab.com/records/arch/high-cohesion.md)
- [Low Coupling](https://banes-lab.com/records/arch/low-coupling.md)
- [Explicit Boundaries](https://banes-lab.com/records/arch/explicit-boundaries.md)
- [Separation of Concerns](https://banes-lab.com/records/arch/separation-of-concerns.md)
- [Composability](https://banes-lab.com/records/arch/composability.md)
- [Replaceability](https://banes-lab.com/records/arch/replaceability.md)
- [Plugin Architecture](https://banes-lab.com/records/arch/plugin-architecture.md)
- [Big Ball of Mud](https://banes-lab.com/records/arch/big-ball-of-mud.md)
- [Shared Mutable State](https://banes-lab.com/records/arch/shared-mutable-state.md)
- [Boundary Leakage](https://banes-lab.com/records/arch/boundary-leakage.md)
- [Fitness Functions](https://banes-lab.com/records/arch/fitness-functions.md)

## Linked from

- [Three encodings](https://banes-lab.com/disciplined-methodology/start/three-encodings.md)
- [The relation ranges](https://banes-lab.com/ontology/schema/the-relation-ranges.md)
