https://github.com/zer0contextlost/drift-scan
Detect architectural drift in multi-language codebases (TypeScript, Python, Go)
https://github.com/zer0contextlost/drift-scan
Last synced: 4 days ago
JSON representation
Detect architectural drift in multi-language codebases (TypeScript, Python, Go)
- Host: GitHub
- URL: https://github.com/zer0contextlost/drift-scan
- Owner: zer0contextlost
- License: mit
- Created: 2026-05-23T01:58:46.000Z (about 1 month ago)
- Default Branch: master
- Last Pushed: 2026-05-23T02:39:40.000Z (about 1 month ago)
- Last Synced: 2026-05-23T04:14:55.777Z (about 1 month ago)
- Language: TypeScript
- Size: 89.8 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# drift-scan
Detect architectural drift in multi-language codebases. Declare layer rules once in `.driftrc.json`; drift-scan parses TypeScript, Python, and Go imports and flags every violation.
```
npm install -g drift-scan
drift scan ./my-project
```
---
## How it works
1. You define **zones** (domain, service, infrastructure, etc.) and which zones each one is allowed to import.
2. drift-scan crawls every `.ts`, `.py`, and `.go` file, extracts their imports, and builds a dependency graph.
3. Any import that crosses a zone boundary in the wrong direction is a violation.
Violations are ranked by blast radius: fanout (how many files in the same zone import the offending file) multiplied by how central the zone is. Scores map to `critical / high / medium / low`.
---
## Config
`.driftrc.json` in the project root:
```json
{
"layers": ["domain", "application", "infrastructure"],
"zones": {
"domain": {
"paths": ["src/domain/**"],
"canImport": []
},
"application": {
"paths": ["src/application/**"],
"canImport": ["domain"]
},
"infrastructure": {
"paths": ["src/infrastructure/**"],
"canImport": ["domain", "application"]
}
},
"ignore": ["**/*.test.ts", "**/*.spec.ts"]
}
```
`drift init [dir]` scaffolds a config by inspecting directory names.
---
## Commands
```
drift scan [dir] scan for violations (default: current directory)
drift explain show all violations involving a specific file
drift graph [dir] print the inter-zone dependency graph
drift stats [dir] architecture health overview
drift init [dir] scaffold a .driftrc.json
```
### scan options
| Flag | Description |
|------|-------------|
| `--json` | Output JSON |
| `--sarif` | Output SARIF 2.1 (GitHub code scanning) |
| `--since ` | Only scan files changed since a git ref (e.g. `main`) |
| `--fail-on ` | Exit 1 if violations at this severity or above exist |
| `--min-severity ` | Only show violations at or above this severity |
| `--output ` | Write report to file instead of stdout |
| `--watch` | Watch for file changes and re-scan automatically |
| `--save-baseline ` | Save current violations as a baseline |
| `--from-baseline ` | Only report violations not present in the baseline |
### graph options
| Flag | Description |
|------|-------------|
| `--dot` | Output Graphviz DOT instead of Mermaid |
| `--output ` | Write graph to file |
---
## Suppressing violations
**Inline** — add a `drift-ignore` comment on the import line or the line above:
```typescript
// drift-ignore
import { db } from '../infra/Database';
import { db } from '../infra/Database'; // drift-ignore
```
Works in TypeScript (`// drift-ignore`), Python (`# drift-ignore`), and Go (`// drift-ignore`).
**Config exceptions** — whitelist specific file paths in `.driftrc.json` without touching source:
```json
{
"exceptions": [
{
"from": "src/domain/legacy/**",
"to": "src/infra/**",
"reason": "migration in progress"
}
]
}
```
Both `from` and `to` are glob patterns (same syntax as zone `paths`). `to` is optional.
---
## Baseline mode
Useful when adopting drift-scan on an existing codebase with pre-existing violations:
```bash
# Snapshot current state
drift scan --save-baseline baseline.drift.json
# CI: only fail on new violations introduced after the baseline
drift scan --from-baseline baseline.drift.json --fail-on high
```
The baseline stores stable fingerprints (violation type + relative file paths + line number). Violations that move to a different line will still match as long as they're the same import.
---
## Violation types
| Type | Description |
|------|-------------|
| `layer` | A zone imports from a zone it is not permitted to depend on |
| `circular` | A cross-zone dependency cycle exists |
| `undeclared` | A file outside any zone imports from a declared zone |
`import type` violations are flagged with `[type-only]` and scored lower — they carry no runtime risk.
---
## Severity
| Score | Severity |
|-------|----------|
| ≥ 12 | critical |
| ≥ 8 | high |
| ≥ 4 | medium |
| < 4 | low |
Score = `fanout × 2 + zone_centrality` (circular: `cycle_length + zone_centrality`).
---
## CI integration
### GitHub Actions — SARIF upload
```yaml
- name: Scan architecture
run: drift scan --sarif --output drift.sarif
- name: Upload to code scanning
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: drift.sarif
```
### Exit code
`drift scan --fail-on high` exits 1 when any `high` or `critical` violation is found.
---
## TypeScript path aliases
drift-scan reads `tsconfig.json` `compilerOptions.paths` and `baseUrl` automatically. Imports like `@domain/User` are resolved to their real paths before zone matching — no extra config needed.
```json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@domain/*": ["src/domain/*"],
"@infra/*": ["src/infra/*"]
}
}
}
```
---
## Language support
| Language | Import styles |
|----------|--------------|
| TypeScript / JavaScript | `import`, `require()`, `import()`, `export … from`, `import type` |
| Python | `import x`, `from x import y`, relative imports |
| Go | `import "github.com/org/repo/internal/pkg"` (module-relative) |
---
## Requirements
- Node.js ≥ 18
---
## License
MIT — [zer0contextlost](https://github.com/zer0contextlost)