An open API service indexing awesome lists of open source software.

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)

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)