ADR-0010 - TypeScript path aliases mirror the discipline-level folders
ADR-0010 - TypeScript path aliases mirror the discipline-level folders #
- Status: Accepted (extended 2026-05-13 by ADR-0040 — fifth alias
@runners/*added) - Date: 2026-05-07
- Deciders: Natan
Context #
src/ has four top-level folders that name the discipline-level concerns of SQA: lib/ (shared infra), components/ (per-external-system capabilities), config/ (validated env + targets), and systems/ (per-system-under-test flow compositions). Per ADR-0009, those folder names are deliberate - they match the words a reader already knows from the discipline.
Until now every cross-folder import was relative:
import { logger } from "../../lib/logger.ts";
import * as api from "../../components/api/ready.ts";PRD-01 lands eight components and 23 sub-items. Two systems and eight components in, the relative paths become ../../../components/<choice>/<verb>.ts with no help from autocomplete and no readability. Three options:
- Keep relatives. Zero config change; readability degrades with
every new folder depth.
- One catch-all alias (
@/* → src/*). One config line; the alias
carries no semantic information - @/components/... reads the same as ../../components/... minus the dot count.
- One alias per discipline-level folder. Four aliases, one per
top-level src/ folder. The alias name matches the folder name, which already matches the discipline word per ADR-0009.
Decision #
Four path aliases, one per top-level src/ folder, named for the folder they point at. Configured in tsconfig.json via baseUrl: "." and paths:
{
"@lib/*": ["src/lib/*"],
"@components/*": ["src/components/*"],
"@config/*": ["src/config/*"],
"@systems/*": ["src/systems/*"],
"@runners/*": ["src/runners/*"] // added 2026-05-13 per ADR-0040
}Two import rules:
- Cross-folder imports use aliases. Any import that reaches into a
different top-level src/ folder goes through the alias matching the destination folder (@lib/..., @components/..., etc.).
- Same-folder imports stay relative. Imports inside a single
top-level folder use ./ (e.g. ./preflight.ts from systems/snappy-api/index.ts). The alias would add no signal where the relative path is already one segment long.
Consequences #
- Pro: Cross-folder imports read as discipline words
(@components/api/ready.ts, @lib/logger.ts) - the alias names the layer the reader already knows from ADR-0009.
- Pro: Folder renames are a one-line
tsconfig.jsonchange. The
alias names absorb the rename; call sites don't move.
- Pro:
grep -rn '\.\./' src/is now a sharp invariant - any
match means a cross-folder import slipped past the rule.
- Pro: Bun resolves
tsconfig.jsonpathsnatively at runtime;
no bundler / build step needed.
- Con: Two import styles coexist (
@lib/...vs./logger.ts).
The rule "cross-folder = alias, same-folder = relative" is one line and the grep above catches drift.
- Con: Adding a fifth top-level folder under
src/requires
adding a fifth alias. Cheap; ADR-0009 already constrains when a new top-level folder is justified.
- Falsifiability: Revisit if (a) the same-folder-relative rule
causes a measurable drag (e.g. heavy folder-internal refactoring needs alias support too), or (b) another fifth top-level folder appears whose name shouldn't be a public alias - at which point re-examine the "one alias per folder" mapping.
See also #
- ADR-0040 - adds
the fifth discipline-level folder runners/ and the corresponding fifth alias @runners/*. The Falsifiability bullet in this ADR ("Adding a fifth top-level folder under src/ requires adding a fifth alias") was exercised; the one-line tsconfig.json change anticipated there is exactly what landed.
- ADR-0009 - the
discipline-level folder names this ADR aliases.
- ADR-0001 - three-layer split
(lib / components / systems); config joined as a fourth top-level folder.
docs/prds/01-expand-components-health-probes.md↗- PRD that motivated the rewrite (sub-items
01.0.1and01.0.2).
- PRD that motivated the rewrite (sub-items