research 005: which modules actually change together
The domain-grouping premise is testable, so it was tested before drawing a list. Co-change across the full history of the module catalogue, current modules only, platform namespace excluded. Nine commits in ten touch exactly one module, and 50 of 89 modules have never been edited alongside anything. Two clusters exist above that floor. Reachability holds up: proxy, resolver, firewall and VPN genuinely move together under one intent, three times in recent history. That is the shape ADR 0017 describes and the only place the measurement finds it. The provider cluster does not, and this is the finding worth having. Every multi-provider commit is a cross-cutting manifest change applied N times — feature detection, hook conventions, volume binds, network scoping. None is a change to what a database is. Merging them would not have prevented one of those commits, and the history already shows the fix that worked: verify by shape in the SDK rather than copying a script into every module. Move the concern into the machinery, do not merge the modules carrying it. ADR 0017 keeps its principle and gains a pointer to this narrowing.
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
---
|
||||
effort: 005-domain-grouping
|
||||
updated: 2026-08-23
|
||||
---
|
||||
|
||||
# Which modules actually change together
|
||||
|
||||
## Method
|
||||
|
||||
Every commit in the code repository's main branch that touches the module catalogue, reduced
|
||||
to the set of modules it touched. Platform-namespace modules are excluded — their
|
||||
decomposition is settled by
|
||||
[ADR 0015](../../02-DECISIONS/0015-mesh-brokers-nodes-host-agents-think.md). Modules that no
|
||||
longer exist are excluded, because pre-rename names dominate the raw signal and describe a
|
||||
catalogue nobody works in.
|
||||
|
||||
Commits touching more than eight modules are excluded from pair counting: a sweep across
|
||||
forty modules says the sweep happened, not that the modules are related.
|
||||
|
||||
## Finding 1 — these modules overwhelmingly change alone
|
||||
|
||||
| Measure | Value |
|
||||
|---|---|
|
||||
| Non-platform modules today | 89 |
|
||||
| Commits touching at least one | 312 |
|
||||
| Commits touching two to eight together | 33 — **10%** |
|
||||
| Modules that never co-change with anything | **50 of 89** |
|
||||
|
||||
Nine commits in ten touch exactly one module. More than half the catalogue has never been
|
||||
edited alongside anything else.
|
||||
|
||||
This is the first thing a grouping proposal has to survive, and it is not what the premise
|
||||
predicts.
|
||||
|
||||
## Finding 2 — two clusters exist
|
||||
|
||||
Everything above two co-changes falls into one of two groups.
|
||||
|
||||
**The provisioned infrastructure providers.** The densest cluster: the relational stores with
|
||||
each other and with the cache, the broker, the object store and the image registry — a
|
||||
strongly connected set, six to two co-changes per pair.
|
||||
|
||||
**Reachability.** The reverse proxy with the resolver, the firewall and the VPN; the firewall
|
||||
with the intrusion filter. Three to two co-changes per pair.
|
||||
|
||||
Outside those: a torrent client with the VPN, twice. A login manager with a media player,
|
||||
twice. Nothing else reaches two.
|
||||
|
||||
## Finding 3 — the provider cluster is manifest churn, not cohesion
|
||||
|
||||
This is the result that matters, and it is only visible by reading the commits rather than
|
||||
counting them.
|
||||
|
||||
Every multi-provider commit is a **cross-cutting change to the machinery, applied N times**:
|
||||
|
||||
| Date | What the commit did |
|
||||
|---|---|
|
||||
| 2026-04-15 | modules own their tools — decentralised tool serving |
|
||||
| 2026-04-24 | filesystem-driven feature detection; manifest boilerplate removed |
|
||||
| 2026-04-27 | remove runtime dependencies that were only build dependencies |
|
||||
| 2026-05-01 | hooks move into their own directory with their own package |
|
||||
| 2026-05-05 | hooks renamed to a stage-and-feature convention; legacy install scripts removed |
|
||||
| 2026-08-06 | verify **by shape in the SDK**, not by copying a script into every module |
|
||||
| 2026-08-11 | bind declared volume paths that leaked an anonymous volume per deploy |
|
||||
| 2026-08-23 | scope services to the local network and mesh; respect the profile gate |
|
||||
|
||||
**Not one of them is a change to what a database is.** They are all "the manifest contract
|
||||
changed, therefore every manifest changed".
|
||||
|
||||
Two consequences follow.
|
||||
|
||||
Merging the providers into one module would not have prevented a single one of these commits.
|
||||
It would have made the same edit land in one file rather than six — which is a diff-size
|
||||
improvement, not a boundary.
|
||||
|
||||
And the history already shows the correct fix being applied, repeatedly and successfully:
|
||||
*verify by shape in the SDK, not by copying a script into every module*. When the same edit
|
||||
must be made in every provider, the answer that worked was **moving the concern into the
|
||||
machinery**, not merging the modules that carry it.
|
||||
|
||||
Co-change here measures coupling to the manifest contract. It does not measure domain
|
||||
cohesion, and using it as though it did would group the catalogue by which modules are most
|
||||
boilerplate-heavy.
|
||||
|
||||
## Finding 4 — reachability holds up
|
||||
|
||||
The same reading applied to the reachability cluster gives a different answer. Most of its
|
||||
twenty-five multi-module commits are the same machinery churn — but not all, and the
|
||||
remainder are genuine:
|
||||
|
||||
| Date | What the commit did |
|
||||
|---|---|
|
||||
| 2026-07-29 | let the mesh's proxy coexist with another listener on the same port |
|
||||
| 2026-08-03 | derive public split-DNS from node accessors so requests stop hairpinning |
|
||||
| 2026-08-06 | firewall mesh-only by default, public by declaration |
|
||||
|
||||
Each is one intent — *change how a node is reachable* — landing across the proxy, the
|
||||
resolver, the firewall and the VPN together. That is exactly the shape ADR 0017 describes, and
|
||||
it is the only place in the catalogue where the measurement finds it.
|
||||
|
||||
The 2026-08-23 scoping commit is the sharpest case: it spans the reachability cluster **and**
|
||||
two providers, because "which network is this exposed on" is a reachability question asked of
|
||||
a database.
|
||||
|
||||
## What this means for ADR 0017
|
||||
|
||||
The record's principle stands, and its scope needs narrowing. Grouping by domain is:
|
||||
|
||||
- **Justified by evidence** for reachability. One intent, several modules, repeatedly.
|
||||
- **Argued against by evidence** for the providers. The coupling is to the manifest contract,
|
||||
and the demonstrated fix is to move the concern into the machinery.
|
||||
- **Unsupported either way** for the fifty modules that co-change with nothing. Silence is not
|
||||
evidence of independence — many are simply rarely touched — but there is no measured basis
|
||||
for grouping them, and a proposal that groups them is drawn from intuition. It should say so.
|
||||
|
||||
The catalogue's shape is still wrong in the way the record describes. Measurement says
|
||||
grouping is the right fix in fewer places than the record implies, and that a second fix —
|
||||
moving cross-cutting concerns into the machinery — accounts for most of what looks like
|
||||
grouping pressure.
|
||||
|
||||
## Recommendation on the provisioning reference
|
||||
|
||||
Asked directly, and stated as an opinion because it is not yet decided.
|
||||
|
||||
**Do not group the provider modules.** Three reasons, in order of weight:
|
||||
|
||||
1. **The evidence for grouping them is the wrong evidence.** Finding 3.
|
||||
2. **A requirement names a provider module.** Grouping providers means a consumer names a
|
||||
resource type and something else chooses the implementation. That is not a folder move; it
|
||||
is implementation selection, a substantially larger design with its own failure modes, and
|
||||
nothing currently asks for it.
|
||||
3. **It would hide which implementation serves a requirement** — the one place the mesh most
|
||||
needs to be explicit, and precisely the indirection ADR 0017 warns grouping causes.
|
||||
|
||||
A provider module is already exactly one purpose: it provisions one resource type. That is a
|
||||
boundary, not an accident of installation.
|
||||
|
||||
## Still to do
|
||||
|
||||
- Draw the list for the cases where grouping is justified, and say plainly which entries rest
|
||||
on measurement and which on judgement.
|
||||
- Decide the ~50 silent modules: correctly sized, or unmeasured?
|
||||
- Test the reachability grouping against the migration cost — the modules in it are among the
|
||||
most-changed in the catalogue, so churn during a move is not hypothetical.
|
||||
Reference in New Issue
Block a user