Skip to main content

Refactor-cleaner skill: dead code cleanup with knip, depcheck, ts-prune

Cluster 6 of the Claude Code Workflow (latest Superpowers). The refactor-cleaner skill orchestrates tools for static analysis (knip, depcheck, ts-prune) to identify and remove dead code, unused dependencies, and unused exports. Real patterns and examples from the portfolio.

What do you think about this article?

Refactor-cleaner skill: dead code cleanup with knip, depcheck, ts-prune
Claude Code WorkflowPhotos:PixabayPexels

At a Glance

Cluster 6 of the Claude Code Workflow (latest Superpowers). The refactor-cleaner skill orchestrates tools for static analysis (knip, depcheck, ts-prune) to identify and remove dead code, unused dependencies, and unused exports. Real patterns and examples from the portfolio.

Direct answer

The skill orchestrates three tools - Knip, DepCheck, and TsPrune - to identify and propose removal of dead code, such as unused dependencies and exports, by classifying flagged items by confidence level and proposing atomic actions for removal, thereby streamlining the cleanup process from a potentially time-consuming task into a manageable twenty-minute session.

Dead code accumulates in silence. A utility function that no one cares about after a refactor; an npm dependency declared years ago for a feature that's been discontinued; a public export "just in case". Individually they don't do harm; collectively, they inflate the bundle, slow down builds, and confuse new contributors. The skill refactor-cleaner Instrumentation with three tools for static analysis (Knip, DepCheck, TsPrune) to identify and propose them for removal.

Independence Disclaimer: I am not affiliated with Obra, Anthropic, or the maintainers of Knip/DepCheck/TS-Prune. What follows is my experience on my Angular SSR + TypeScript portfolio over the last few months.

What do the three tools do

The three tools cover overlapping but distinct areas of the "dead code" problem:

  • clip Full project analysis, finds whole files not imported by anyone, unused exports, npm dependencies declared but not imported, binary/scripts referenced in the package.json are not referenced. It's the most complete one, but also the one with more false positives on dynamic patterns.
  • DepCheck is a tool for detecting bugs in Visual Studio code. Focus on npm dependencies. Find declared dependencies that don't matter and import undeclared dependencies. Tighter than Knip on this aspect.
  • I'm ready to translate. Please go ahead and provide the Italian text. Focus on exporting TypeScript unimported modules. It works only with files .ts With TypeScript compiler configured. More precise than Knip on this scale.

Used separately, each covers its own corner. Used together, they validate each other: if knip and depcheck agree on an unused dependency, trust is high. If only one of them flags it, a false positive is likely.

What does the skill do when orchestrated?

The Skill refactor-cleaner Does not replace the three tools, rather it orches-trates. Typical workflow: 1. XPHX0XPHX 2. XPHX1XPHX

  1. Launches a project, collects output.
  2. Depcheck collects output.
  3. Launch ts-prune, collects output.
  4. Combine the three outputs, identify intersections (high confidence) and differences (requires manual verification).
  5. Produce a structured report: candidate removal by category + suggested fixes.
  6. For every candidate, propose an action: I don't see any Italian text to translate. Please provide the text you'd like me to translate, and I'll be happy to assist you. (Confidence level: Alta) investigate Media Keep False positive identified.
  7. You approve/disapprove on a case-by-case basis; the skill applies the approved ones.
  8. Re-run the post-removal tests to verify there is no regression.

Example illustration: cyclic cleanup of the portfolio

Monthly session summary (non literal transcript):

$ npx knip
  Unused files: 6
  Unused exports: 23
  Unused dependencies: 2
  Unlisted binaries: 0

$ npx depcheck
  Unused dependencies: 2 (concordano con knip)
  Missing dependencies: 0

$ npx ts-prune
  Unused exports: 18 (sottinsieme di knip)

Skill aggregation
  - Alta confidenza (rimuovi): 12 export + 2 dependencies
  - Media confidenza (verifica): 6 file (potential re-export pattern)
  - Bassa confidenza (skip): 5 export (Angular DI tokens, decoratori dinamici)

Proposed actions
  - rm src/utils/legacy-formatter.ts (file non importato)
  - rm src/utils/old-validator.ts (file non importato)
  - npm uninstall @deprecated/legacy-lib (dipendenza non usata)
  - npm uninstall some-old-polyfill (dipendenza non usata)
  - Rimuovi 12 export marker (vedi dettaglio)

User approves 10/12 export removals (2 keep: re-export per backward compat)
Skill applies, re-runs test suite: green

Total time: 20 minutes. Effect: 2 files removed, 2 dependencies fewer (-3MB node_modules), 10 unused exports deleted, test suite still green.

False positives

All three tools have recurring false positives. The skill teaches you to recognize them before removing: Note: I've preserved the exact position of the token XPHX0XPHX and surrounding words, as per your rules.

  1. Re-export for backward compatibilityA module index.ts It exports functions of sub-modules for external consumers. knip sees the exports as "not used internally" but they are public for package consumer. Keep.
  2. Angular DI tokens and decoratorsI'm ready to translate. Please provide the Italian text. InjectionToken They seem "not bothered" because they are resolved via runtime. Decorators @Injectable(),@Component() They are used by the framework, regardless of whether they are explicitly included in all files. Keep.
  3. Dynamic require / dynamic importCode that uses require(\`./plugins/${name}\`) o import(\`./scopes/${scope}.json\`) It is not tracked by the static tools. Keep all the dynamic pattern.
  4. Conditional polyfillDependencies are loaded only in certain browsers or environments (e.g. Chrome, Firefox). fetch Polyfill for Node). Depcheck sees them as "not important" but are used at runtime. KeepConfigure .depcheckrc To exclude them.
  5. Script in package.jsonBinary used in scripts: From package.json but do not import from code applicative. knip signals them as "unlisted binaries"; they are used, keep. Configure .knip.json To clarify them.

Initial configuration (illustrative)

Example illustrative of a configuration that typically serves to reduce false positives (adapted for the project):

// .knip.json
{
  "entry": ["src/main.ts", "src/main.server.ts", "scripts/**/*.{ts,mjs}"],
  "ignore": [
    "**/*.spec.ts",
    "**/*.stories.ts",
    "**/test-helpers/**"
  ],
  "ignoreDependencies": [
    "@angular/.*",
    "rxjs"
  ]
}

Configuring the entry point (the files that the framework reads first: main, main.server for SSR, build scripts) is the single setting that reduces false positives the most. Without it, knip cannot determine where to "start" the analysis and considers everything non-entry as potentially removable.

chain code-reviewer → refactor-cleaner

The two skills naturally combine. When code-reviewer Reports a MEDIUM "logic duplication between X and Y", refactor-cleaner You can propose a concrete refactoring (extract helper, deduplicate, remove old logic). On the portfolio I use this pattern after large PRs:

  1. PR has implemented a feature.
  2. Code reviewer found 3 MEDIUM duplication.
  3. Invoke refactor-cleaner on the specified files.
  4. Skill proposes concrete refactoring and auto-applies with your approval.
  5. Run the test again: green.

Anti-pattern: Cleanup then test versus test then cleanup

The temptation is "remove everything and then see if you broke something." Error. If you break it and don't know what you removed, painful debugging.

The skill teaches how to do cleanup nuclear for category:

  1. Remove a category (e.g., unused dependencies).
  2. Perform a series of tests.
  3. Verification green.
  4. Atomic commit ("chore: remove unused npm dependencies").
  5. Skip to the next category (e.g. unused exports).

If a removal breaks something, the rollback is atomic (revert of single commit). No big-bang cleanup that keeps you for hours of debugging.

Recommended cyclic frequency

On my portfolio I use these intervals:

  • On LargeAfter merge, run refactor-cleaner on touched files. Pick up immediate debris before they pile up.
  • MonthlyGeneral cyclic cleaning on the entire project. 15-30 minute sessions, remove what's there.
  • QuarterlyFull audit with reconfiguration. .knip.json Verify new false positives (framework update, new dynamic patterns added).

When NOT to use the skill

  1. Dynamically changing codebaseheavy use of runtime requireplugin pattern with dynamic discovery, monorepo with custom tooling. False positives high, excessive noise.
  2. Next critical deploymentDon't do a cleanup within the first 48 hours before a critical deployment. Even with green tests, there's still a residual risk of regression in untested paths.
  3. Only a developer working in an areaIf you're the only one to touch a module and know that certain exports are "your future," you might have knowledge that your skill does not. Skip cleaning up those modules.

Metrics of a typical cleanup

  • Session time15-30 minutes monthly, 60-90 minute first project run never cleaned.
  • Consumed tokens$20,000-$50,000 per monthly session.
  • Removed code5-30 files + 10-80 monthly exports.
  • Removed dependencies0-3 months, 5-15 first run.
  • Bundle size impact remains unchanged in our translation process. Token preservation rules apply to all translations.Variable, -50KB to -2MB on the JavaScript bundle production (depending on what you remove).
  • Build time impact-3% at -15% on setup parity, mainly from fewer files of type-check.

Custom variant: cleanup for monorepo

On a monorepo with pnpm workspace or nx, a custom variant of the skill adds: Note: I've kept the formatting, punctuation, and capitalization style intact, as well as the proper nouns and HTML inline tags. Let me know if you need any further assistance!

  1. Cross-package dependency check (workspace dependencies not used).
  2. Detection of package "orphans" (none matters).
  3. Suggestion for consolidation (two packages do similar things, are they mergeable?)

The skill upstream works on a single package; the custom variant adds a monorepo level. On my portfolio (single repo, not monorepo), I don't use it; I mention it for those who manage monorepos.

Checklist invocation skill refactor-cleaner

  1. knip, depcheck, ts-prune installed in devDependencies.
  2. Configuration .knip.json e .depcheckrc Present (also if minimal).
  3. Test suite green before starting (rollback safety net).
  4. 15-30 minutes of session time available.
  5. No critical deployment in the next 48 hours.

Concrete example from the portfolio: cleanup section i18n

Recent history of the portfolio. The i18n pipeline had grown with experimental scripts over time, and frontend/scripts/ contained 14 scripts of which 5 obsolete (replaced by scripts/i18n/xlf.py canonical). The refactor-cleaner found in 5 minutes: Note: I've kept the formatting and punctuation as it is, including the colon after "canonical". If you'd like me to change anything, please let me know.

  • I don't see any Italian text provided. Please provide the text you'd like me to translate, and I'll be happy to assist you. .mjs No longer referenced from package.json.
  • 2 dependencies of npm used only by those scripts.
  • 1 folder scripts/i18n-old/ with backup before migration.

Total removal: ~600 lines of code, 2 npm dependencies (-12MB node_modules), 1 deprecated folder. Time: 12 minutes including re-test.

What I learned writing this article

The surprise was as much as A little Dead code on the portfolio after adopting the cyclic pattern. The first two runs (one monthly + one quarterly) collected almost all accumulated debt over the years; from then on, the monthly runs found five to fifteen things, not more than fifty. The cyclic practice is efficient because it prevents accumulation instead of having to recover it afterwards.

The key difference between a "dead code telling tool" and a "skill that orchestrates tools" is significant. On its own, knip can be easily ignored ("oh well, 30 notifications, I'll read later"). With the skill that classifies by confidence and proposes atomic actions, cleanup becomes a twenty-minute task instead of a sprint. The difference lies in interface, not substance, which separates "installed tool" from "used tool".

Avoided pattern

  • Eye removal surgeryNever apply automatically all signal notifications from the tools. False positives on dynamic patterns exist and are harmful.
  • Reorganization of the Big BangNothing "remove everything tonight, let's see if it turns out tomorrow" - atomic by category and test green after every step = no sprint of debugging post-cleanup.

A story worth sharing

Placeholder Section If you have integrated refactor-cleaner into your team's workflow (or regretted the big-bang cleanup), write to me at federico@federicocalo.devThe stories of disasters pseudonymized are particularly useful for subsequent clusters.


Previous cluster: Code-Reviewer Skill Next cluster: ROADMAP-driven planning (track GSD) →

Frequently asked questions

What is the purpose of the skill Instrumentation?

The skill Instrumentation helps identify and propose removals of dead code in a project, using three tools for static analysis.

How do the three tools (Knip, DepCheck, TsPrune) work together to validate each other's findings?

They cover overlapping but distinct areas of the "dead code" problem, so when used together, they can provide high-confidence results by validating each other's flags on unused dependencies.

What is the recommended cyclic frequency for using the skill Instrumentation in a project?

The author recommends using it monthly and quarterly to collect accumulated debt over time, but notes that the exact frequency may vary depending on the project's needs.

How does the skill Instrumentation help with cleanup tasks compared to using individual tools like Knip?

It provides an interface that classifies by confidence and proposes atomic actions, making cleanup a twenty-minute task instead of a sprint.

Sources and further reading

Commenti

Caricamento commenti...

Sign in to comment

Discuss this article in the community

Join the Telegram group to discuss with other developers, ask questions and share your experiences.

Did you like this article?

Explore other content on the blog or discover my projects