Architecture Erodes One Import at a Time

drift-scan parses TypeScript, Python, and Go imports against declared zone rules and ranks every violation by blast radius.

Nobody merges a PR that says “let the infrastructure layer reach into domain logic.” It happens one import at a time, each one reasonable in isolation, until the dependency graph looks nothing like the diagram in the design doc. Code review catches the big violations. It misses the accumulation.

drift-scan makes the rules explicit and checks every import against them. You declare zones in .driftrc.json — domain, application, infrastructure, whatever your system uses — and for each zone, which other zones it’s allowed to import from. drift init will scaffold a starting config by inspecting your directory names.

From there, drift scan crawls every .ts, .py, and .go file, extracts their imports, and builds a dependency graph. Any import that crosses a zone boundary in the wrong direction is a violation. Three types get flagged: layer (a zone importing from one it isn’t permitted to depend on), circular (a cross-zone dependency cycle), and undeclared (a file outside any zone reaching into one that’s declared).

Not every violation is equally urgent. drift-scan scores each one as fanout times two plus zone centrality (cycle length plus zone centrality for circular violations), then maps that score to critical, high, medium, or low. A violation in a file that fifteen other files in its zone import from is a different problem than one in a file nobody touches. import type statements get flagged too, but scored lower and tagged [type-only] since they carry no runtime risk.

For adoption on an existing codebase, drift scan --save-baseline snapshots current violations, and --from-baseline filters them out of future scans. The baseline uses stable fingerprints keyed on violation type and relative file paths, so a violation surviving a refactor that moves it to a different line still matches. You don’t have to fix everything on day one. You just have to stop it from getting worse.

CI integration is drift scan --sarif --output drift.sarif piped into GitHub’s code scanning upload, or --fail-on high to exit 1 on the violations that matter. --since <ref> scopes a scan to files changed against a git ref, which is what you actually want in a PR check — the full codebase’s pre-existing debt isn’t your problem this PR, the twelve lines you just wrote are.

TypeScript path aliases resolve automatically: drift-scan reads tsconfig.json’s compilerOptions.paths and baseUrl, so @domain/User maps to its real path before zone matching runs. No separate alias config to maintain.

Full paper →

↑