I've been vibe coding for months. Fast shipping, AI pair programming, features done in hours.
At some point I sat down and actually read the code the AI had written.
It worked. But it had left a consistent pattern of silent debt — the kind CI doesn't catch, linters don't flag, but makes the codebase progressively harder to work with.
Things like:
-
catch(e) {}— errors swallowed silently with no logging, no rethrow - Unused imports from refactors the AI did and never cleaned up
- Functions with no explicit return types breaking IDE inference
-
console.logburied 3 layers deep in business logic - 50-line functions doing 5 unrelated things
-
TODO/FIXMEcomments the AI added as placeholders and never resolved
None of it broke anything. All of it made the codebase worse.
So I built drift.
What it does
drift is a CLI that scans your TypeScript project using AST analysis (ts-morph) and scores each file from 0–100 based on AI-generated debt patterns.
npx @eduardbar/drift scan ./src
Output:
drift — vibe coding debt detector
Score 67/100 HIGH
4 file(s) with issues · 5 errors · 12 warnings · 3 info
src/api/users.ts (score 85/100)
✖ L1 large-file File has 412 lines (threshold: 300)
▲ L34 debug-leftover console.log left in production code
▲ L89 catch-swallow Empty catch block silently swallows errors
▲ L201 any-abuse Explicit 'any' type detected
src/utils/helpers.ts (score 70/100)
✖ L12 duplicate-function-name 'formatDate' looks like a duplicate
▲ L55 dead-code Unused import 'debounce'
The 8 detection rules
| Rule | Severity | What it catches |
|---|---|---|
large-file |
error | Files over 300 lines — AI dumps everything into one place |
large-function |
error | Functions over 50 lines — AI avoids splitting logic |
debug-leftover |
warning |
console.log, TODO, FIXME, HACK comments |
dead-code |
warning | Unused imports — AI imports more than it uses |
duplicate-function-name |
error | Near-identical function names — AI regenerates instead of reusing |
any-abuse |
warning | Explicit any — AI defaults to any when it can't infer |
catch-swallow |
warning | Empty catch blocks — AI makes code "not throw" |
no-return-type |
info | Missing explicit return types on functions |
Score breakdown
| Score | Grade | Meaning |
|---|---|---|
| 0 | CLEAN | No issues found |
| 1–19 | LOW | Minor issues, safe to ship |
| 20–44 | MODERATE | Worth a review before merging |
| 45–69 | HIGH | Significant structural debt |
| 70–100 | CRITICAL | Review before this goes anywhere near prod |
Validation
I ran it on the shadcn/ui CLI source — professional human-written code. Score: 0.
I ran it on my own vibe-coded modules. Score: 40–60 consistently.
The difference is real. The tool makes it measurable.
CI integration
- name: Check for vibe coding drift
run: npx @eduardbar/drift scan ./src --min-score 60
Exit code 1 if score exceeds --min-score. Blocks the merge. Done.
Known limitation
console.log in intentional CLI output files gets flagged as debug-leftover. A .drift.config.ts to mark exceptions is on the roadmap.
Stack
TypeScript · ts-morph · Commander.js · kleur · zero runtime dependencies
Zero config. MIT. Works on any TypeScript or JavaScript project.
GitHub: github.com/eduardbar/drift
npm: npx @eduardbar/drift scan .
If you're shipping AI-generated code — run this before your next deploy. You'll be surprised.
Top comments (0)