Skip to main content
Cover image for Pruning a Codebase with Knip

Pruning a Codebase with Knip

tooling javascript maintenance

I ran npx knip on my Next.js project. It found 109 files that were never imported. I deleted them all—15,790 lines of code gone in a single commit.

What Is Knip?

Knip detects unused files, exports, and dependencies. It traces imports from your entry points (app/layout.tsx, next.config.js) and flags anything unreachable. For Next.js projects, it works out of the box.

npx knip

The output lists everything that can be removed. My list was extensive.

What I Found

The dead code came from abandoned features:

  • Chrome Extension (908 lines): I built an extension to scrape job postings, but later switched to a different flow. The extension’s UI and background scripts remained in the repo for months.
  • Document Management (1,477 lines): A feature to let users attach resumes to job applications. It was built and shipped, but usage was low. I removed the UI but left the underlying components.
  • 20 shadcn/ui components (~2,400 lines): I installed a carousel, chart, command palette, and more, thinking I would need them. I never did.

The rest was scattered across hooks, utilities, type definitions, and forgotten scripts.

Most cleanup concentrated here:

components/ui/     38 files
components/        23 files
lib/               17 files
scripts/           12 files
hooks/              9 files
lib/types/         10 files
lib/validation/     7 files
chrome-extension/   6 files

How I Cleaned It Up

Three passes:

  1. Delete obviously dead code: Components for features that no longer exist, old database migration scripts
  2. Verify the rest: Some components are imported dynamically. I searched for filenames and export names to confirm they were truly unused
  3. Run Knip again: Deleting files can orphan dependencies. I repeated until the report was clean

Final commit: 194 files touched—109 deletions, 85 edits to remove newly unused imports.

Why Dead Code Is Expensive

The problem isn’t disk space—it’s maintenance burden.

I was fixing type errors in files that were never imported. Refactoring validation schemas that no route used. TypeScript errors in dead code still block builds. Search results were cluttered with components that didn’t run. Every refactor felt risky because I couldn’t tell what was load-bearing.

Now the codebase matches what’s actually in production.

Going Forward

I added a knip script to package.json:

{
  "scripts": {
    "lint:unused": "knip"
  }
}

I run it after removing features and before major refactors. The goal isn’t zero warnings—some unused exports are intentional—but to know what’s dead before I waste time maintaining it.