DEV Community

Cover image for drift: an open source CLI that detects silent technical debt in AI-generated TypeScript code
Eduard Barrera
Eduard Barrera

Posted on

drift: an open source CLI that detects silent technical debt in AI-generated TypeScript code

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.log buried 3 layers deep in business logic
  • 50-line functions doing 5 unrelated things
  • TODO/FIXME comments 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
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)