Skip to content
SQA Cockpit

ADR-0010 - TypeScript path aliases mirror the discipline-level folders

ADRsUpdated 3 min readEdit on GitHub ↗

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:

ts
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:

  1. Keep relatives. Zero config change; readability degrades with

every new folder depth.

  1. One catch-all alias (@/* → src/*). One config line; the alias

carries no semantic information - @/components/... reads the same as ../../components/... minus the dot count.

  1. 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:

json
{
  "@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.json change. 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.json paths natively 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 #

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.

discipline-level folder names this ADR aliases.

(lib / components / systems); config joined as a fourth top-level folder.

Was this page helpful?