# The filesystem is the architecture

> Every extension point in the tree is a file in a directory, so adding a capability means adding a file, and removing one means deleting a file.

Page: Methodology · Build
Canonical: https://banes-lab.com/disciplined-methodology/build#the-filesystem-is-the-architecture

This section is stop 37 of 102 in the learning route. Previous: [22 - One home](https://banes-lab.com/disciplined-methodology/build/one-home.md). Next: [24 - Fail at the boundary](https://banes-lab.com/disciplined-methodology/build/fail-at-the-boundary.md).

Every [extension point](https://banes-lab.com/records/arch/extension-points.md) in the tree is a file in a directory, so adding a capability means adding a file, and removing one means deleting a file. A list of what exists that is maintained by hand is a registry in disguise, and that is where the next inconsistency appears, as shown in [G1·b wired then collected](https://banes-lab.com/disciplined-methodology/build#the-filesystem-is-the-architecture-panel-b). The same shape governs pages, rules, checks, templates and documents, typed as shown in [G1·a the registry](https://banes-lab.com/disciplined-methodology/build#the-filesystem-is-the-architecture-panel-a). The [registry pattern](https://banes-lab.com/records/arch/registry-pattern.md), filled by [auto-discovery](https://banes-lab.com/records/arch/auto-discovery.md), is the [open/closed principle](https://banes-lab.com/records/arch/open-closed.md) made physical, and it grows into a [plugin architecture](https://banes-lab.com/records/arch/plugin-architecture.md).

### Adding is adding a file

Every switch over a kind, every record literal of variants and every import list in a composer is a list that you or the model have to remember to update. A new variant renders through the fallback branch because the switch that dispatches it never learned its name, and nothing reported that. A list is complete on the day it is written, and nothing compares it against the directory afterwards.

For this reason the directory is the registry, and a core file that has to learn a name is the wrong design. Each variant registers itself from its own file, rather than being added by hand to a list in a core file. In practice, each variant has its own file that registers itself at module scope, and the files are collected by pattern, never by name. The switch, the lookup table and the list of imports that had to learn each new name are deleted. Where a barrel file collects the files, it is generated from the directory rather than written by hand, and it is checked in both directions: for an entry with no file and for a file with no entry.

To check this, add a variant by adding one file and touching nothing else. If it needed a second edit, the registry is still in disguise. A surface discovered by shape is the riskiest thing in any rename, because a change of suffix quietly changes what it collects. The collection is compared before and after every move, as described in [moves and renames](https://banes-lab.com/disciplined-methodology/verify/moves-and-renames.md), and a count that dropped to zero means the move broke something, not that it was clean.

A barrel file that lists its side-effect imports by hand is the same disguise one level down, and the rules follow the same approach, as described in [a check matches a shape](https://banes-lab.com/disciplined-methodology/build/a-check-matches-a-shape.md).

A generated index over authored data is regenerated from its source and checked for drift in the gate, never edited by hand. Every scan works at any depth, because a scan fixed to one depth reports a pass over whatever sits one level below it. The index is checked in both directions because checking only one direction is decoration: the failure that actually happens is a scan that resolves a smaller set than it claims, with the difference passing for coverage.

The registry has the same shape wherever it appears, and its kind is a closed union, so a variant of an undeclared kind fails to compile, and a lookup for one cannot be written. The same three parts appear whether the variants are pages, checks, codemods or document forms, which is why one primitive serves all of them, and a second registry implementation is a duplicate rather than a convenience.

G1·a the registry

```typescript
export interface Variant<Kind extends string> {
readonly kind: Kind;
readonly applies: (subject: Subject) => boolean;
readonly run: (subject: Subject) => Finding[];
}

export interface Registry<Kind extends string> {
readonly register: (variant: Variant<Kind>) => void;
readonly all: () => readonly Variant<Kind>[];
readonly get: (kind: Kind) => Variant<Kind>;
}

export const checks = createRegistry<CheckKind>();

checks.register({ kind: "unreachable-export", applies: isModule, run: findUnreachableExports });
```

G1·b wired then collected

```mermaid
flowchart TB
subgraph before["Before · the router learns every name"]
router1["router · switch on the page name"]
router1 --> home1["home page"]
router1 --> terms1["terms page"]
router1 --> faq1["faq page"]
end
subgraph after["After · the directory is the registry"]
pages["pages folder · collected by pattern"]
home2["home page · registers itself"] --> pages
terms2["terms page · registers itself"] --> pages
faq2["faq page · registers itself"] --> pages
pages --> router2["router · never learns a name"]
end
```

## Links to

- [Extension Points](https://banes-lab.com/records/arch/extension-points.md)
- [Registry Pattern](https://banes-lab.com/records/arch/registry-pattern.md)
- [Auto-Discovery](https://banes-lab.com/records/arch/auto-discovery.md)
- [Open/Closed Principle (OCP)](https://banes-lab.com/records/arch/open-closed.md)
- [Plugin Architecture](https://banes-lab.com/records/arch/plugin-architecture.md)
- [Moves and renames](https://banes-lab.com/disciplined-methodology/verify/moves-and-renames.md)
- [A check matches a shape](https://banes-lab.com/disciplined-methodology/build/a-check-matches-a-shape.md)

## Linked from

- [A check matches a shape](https://banes-lab.com/disciplined-methodology/build/a-check-matches-a-shape.md)
- [Moves and renames](https://banes-lab.com/disciplined-methodology/verify/moves-and-renames.md)

## Evidence in the code

- [registerPage](https://banes-lab.com/source/tree/domain/registries/page.registry.ts.md)
- [presentation/records](https://banes-lab.com/anatomy/tree/folder-presentation-records.md)
- [records.barrel.ts](https://banes-lab.com/source/tree/presentation/records/records.barrel.ts.md)
