<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bart Waardenburg</title>
    <description>The latest articles on DEV Community by Bart Waardenburg (@bartwaardenburg).</description>
    <link>https://dev.to/bartwaardenburg</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F338786%2Fe4b6fde5-58ba-41ce-8c51-de5495fa3764.jpeg</url>
      <title>DEV Community: Bart Waardenburg</title>
      <link>https://dev.to/bartwaardenburg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bartwaardenburg"/>
    <language>en</language>
    <item>
      <title>I built a Rust-based codebase analyzer that finds dead code in JS/TS projects in milliseconds</title>
      <dc:creator>Bart Waardenburg</dc:creator>
      <pubDate>Mon, 23 Mar 2026 15:27:34 +0000</pubDate>
      <link>https://dev.to/bartwaardenburg/i-built-a-rust-based-codebase-analyzer-that-finds-dead-code-in-jsts-projects-in-milliseconds-180i</link>
      <guid>https://dev.to/bartwaardenburg/i-built-a-rust-based-codebase-analyzer-that-finds-dead-code-in-jsts-projects-in-milliseconds-180i</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;a href="https://github.com/fallow-rs/fallow" rel="noopener noreferrer"&gt;fallow&lt;/a&gt; is a Rust-native static analyzer for JS/TS codebases. It finds unused files, unused exports, unused dependencies, circular dependencies, and duplicated code. Zero config, 84 framework plugins auto-detected, sub-second on most projects. &lt;code&gt;npx fallow check&lt;/code&gt; to try it now.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem that kept bugging me
&lt;/h2&gt;

&lt;p&gt;Every codebase I've worked on has dead code. Exports that nothing imports. Dependencies in &lt;code&gt;package.json&lt;/code&gt; that no file uses. Files that were "temporarily" left in after a refactor six months ago. Entire utility modules where 3 out of 12 functions are actually called.&lt;/p&gt;

&lt;p&gt;That dead code isn't free. It bloats your bundle, slows your builds, and confuses anyone new to the project because they can't tell what's actually used and what's left over from two refactors ago. And the longer nobody touches it, the scarier it gets to delete.&lt;/p&gt;

&lt;p&gt;This was annoying but manageable when humans wrote all the code. Now AI coding agents generate code at a pace where nobody can keep track of what's still needed. They create new files, refactor existing ones, but never go back to clean up what they left behind. They can't, really. Determining whether an export is unused requires building a complete module graph across every file in the project, which is a graph traversal problem, not a "read the file and figure it out" problem.&lt;/p&gt;

&lt;p&gt;Existing tools work, but they were slow enough that I'd only run them occasionally. So I built fallow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;Fallow builds the full import/export graph of your project and reports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unused files, exports, types, enum members, class members&lt;/li&gt;
&lt;li&gt;Unused dependencies and devDependencies&lt;/li&gt;
&lt;li&gt;Unresolved imports and unlisted dependencies&lt;/li&gt;
&lt;li&gt;Duplicate exports (same symbol from multiple modules)&lt;/li&gt;
&lt;li&gt;Circular dependencies&lt;/li&gt;
&lt;li&gt;Code duplication (4 detection modes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;12 issue types total. Here's what it looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;fallow check
&lt;span class="go"&gt;
● Unused files (16)
  src/server/jobs/worker.ts
  src/server/jobs/cron.ts
  src/features/savings/hooks/usePotGroups.ts
  ... and 13 more

● Unused exports (20)
  src/components/Card/index.ts
    :1 CardFooter
  src/providers/trpc-provider/index.tsx
    :12 TRPCProvider

● Unused dependencies (1)
  @trpc/react-query

● Duplicate exports (50)
  ... across 23 files

Found 401 issues in 0.16s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last line is the part I'm most proud of. 0.16 seconds for 401 issues across a real-world codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Rust
&lt;/h2&gt;

&lt;p&gt;I didn't pick Rust to be trendy. The bottleneck in dead code analysis is parsing every file in the project to build the module graph. Fallow uses the &lt;a href="https://oxc.rs/" rel="noopener noreferrer"&gt;Oxc parser&lt;/a&gt; natively in Rust (not through NAPI bindings) and &lt;a href="https://github.com/rayon-rs/rayon" rel="noopener noreferrer"&gt;rayon&lt;/a&gt; for parallel file processing across all cores. If you're already looking at &lt;a href="https://oxc.rs/docs/guide/usage/linter" rel="noopener noreferrer"&gt;oxlint&lt;/a&gt; or &lt;a href="https://oxc.rs/docs/guide/usage/formatter" rel="noopener noreferrer"&gt;oxfmt&lt;/a&gt;, fallow is the same family. Oxlint replaces ESLint, oxfmt replaces Prettier, fallow finds what you're not using anymore.&lt;/p&gt;

&lt;p&gt;Here's how that plays out against knip, the most popular JS/TS dead code analyzer:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Project&lt;/th&gt;
&lt;th&gt;Files&lt;/th&gt;
&lt;th&gt;fallow&lt;/th&gt;
&lt;th&gt;knip v5&lt;/th&gt;
&lt;th&gt;knip v6&lt;/th&gt;
&lt;th&gt;vs v5&lt;/th&gt;
&lt;th&gt;vs v6&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;zod&lt;/td&gt;
&lt;td&gt;174&lt;/td&gt;
&lt;td&gt;19ms&lt;/td&gt;
&lt;td&gt;639ms&lt;/td&gt;
&lt;td&gt;334ms&lt;/td&gt;
&lt;td&gt;34x&lt;/td&gt;
&lt;td&gt;18x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;preact&lt;/td&gt;
&lt;td&gt;244&lt;/td&gt;
&lt;td&gt;20ms&lt;/td&gt;
&lt;td&gt;819ms&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;41x&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fastify&lt;/td&gt;
&lt;td&gt;286&lt;/td&gt;
&lt;td&gt;24ms&lt;/td&gt;
&lt;td&gt;1.13s&lt;/td&gt;
&lt;td&gt;289ms&lt;/td&gt;
&lt;td&gt;46x&lt;/td&gt;
&lt;td&gt;12x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vue/core&lt;/td&gt;
&lt;td&gt;522&lt;/td&gt;
&lt;td&gt;63ms&lt;/td&gt;
&lt;td&gt;702ms&lt;/td&gt;
&lt;td&gt;299ms&lt;/td&gt;
&lt;td&gt;11x&lt;/td&gt;
&lt;td&gt;5x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TanStack/query&lt;/td&gt;
&lt;td&gt;901&lt;/td&gt;
&lt;td&gt;148ms&lt;/td&gt;
&lt;td&gt;2.75s&lt;/td&gt;
&lt;td&gt;1.41s&lt;/td&gt;
&lt;td&gt;19x&lt;/td&gt;
&lt;td&gt;10x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;svelte&lt;/td&gt;
&lt;td&gt;3,337&lt;/td&gt;
&lt;td&gt;325ms&lt;/td&gt;
&lt;td&gt;1.93s&lt;/td&gt;
&lt;td&gt;860ms&lt;/td&gt;
&lt;td&gt;6x&lt;/td&gt;
&lt;td&gt;3x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;next.js&lt;/td&gt;
&lt;td&gt;20,416&lt;/td&gt;
&lt;td&gt;1.48s&lt;/td&gt;
&lt;td&gt;—*&lt;/td&gt;
&lt;td&gt;—*&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Knip v6 adopted the Oxc parser through NAPI bindings and got a lot faster. But it's still single-threaded and still runs in Node.js. The gap narrows on bigger projects, but fallow stays sub-second even at 3,000+ files. On the Next.js monorepo (20,416 files), the existing tooling just crashes without producing results. I keep going back to that repo to make sure fallow actually works on real projects, not just benchmarks that fit neatly in memory. *knip errors out on next.js without valid results.&lt;/p&gt;

&lt;p&gt;Memory usage is 3-15x lower too, which matters if you're running this in CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Duplication detection built in
&lt;/h2&gt;

&lt;p&gt;Most projects I've worked on also have a copy-paste problem. fallow has a &lt;code&gt;dupes&lt;/code&gt; command that finds duplicated code blocks using a suffix array algorithm (no quadratic pairwise comparison):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;fallow dupes
&lt;span class="go"&gt;
Clone group 1 (42 lines, 3 instances)
├─ src/features/forecasting/server/procedures/analytics.ts:141-181
├─ src/features/forecasting/server/procedures/cashflow.ts:153-194
└─ src/features/forecasting/server/procedures/income.ts:590-631

Duplication: 19.4% (27,255 duplicated lines across 398 files)
Found 1,184 clone groups, 2,959 instances (0.23s)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four detection modes: &lt;code&gt;strict&lt;/code&gt; (exact), &lt;code&gt;mild&lt;/code&gt; (default), &lt;code&gt;weak&lt;/code&gt; (different string literals), and &lt;code&gt;semantic&lt;/code&gt; (catches renamed variables too). Clone families group related duplicates and suggest whether to extract a function or a module.&lt;/p&gt;

&lt;p&gt;vs jscpd:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Project&lt;/th&gt;
&lt;th&gt;fallow&lt;/th&gt;
&lt;th&gt;jscpd&lt;/th&gt;
&lt;th&gt;Speedup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;zod&lt;/td&gt;
&lt;td&gt;46ms&lt;/td&gt;
&lt;td&gt;909ms&lt;/td&gt;
&lt;td&gt;20x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;preact&lt;/td&gt;
&lt;td&gt;44ms&lt;/td&gt;
&lt;td&gt;1.33s&lt;/td&gt;
&lt;td&gt;30x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fastify&lt;/td&gt;
&lt;td&gt;84ms&lt;/td&gt;
&lt;td&gt;2.83s&lt;/td&gt;
&lt;td&gt;34x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vue/core&lt;/td&gt;
&lt;td&gt;120ms&lt;/td&gt;
&lt;td&gt;3.13s&lt;/td&gt;
&lt;td&gt;26x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;svelte&lt;/td&gt;
&lt;td&gt;400ms&lt;/td&gt;
&lt;td&gt;3.63s&lt;/td&gt;
&lt;td&gt;9x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;next.js&lt;/td&gt;
&lt;td&gt;3.16s&lt;/td&gt;
&lt;td&gt;24.64s&lt;/td&gt;
&lt;td&gt;8x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Zero config, 84 plugins
&lt;/h2&gt;

&lt;p&gt;Fallow reads your &lt;code&gt;package.json&lt;/code&gt; and auto-detects your framework stack. Next.js, Vite, Vitest, Playwright, Storybook, ESLint, Tailwind, Prisma, Drizzle, Turborepo, Nx... 84 plugins total, 31 of which do deep config file parsing (reading your &lt;code&gt;next.config.js&lt;/code&gt;, &lt;code&gt;vite.config.ts&lt;/code&gt;, etc. to understand entry points and aliases).&lt;/p&gt;

&lt;p&gt;No &lt;code&gt;.fallowrc.json&lt;/code&gt; required unless you want to customize. Just run it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auto-fix
&lt;/h2&gt;

&lt;p&gt;Fallow can remove unused exports, unused dependencies, and unused enum members automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Preview what would change&lt;/span&gt;
fallow fix &lt;span class="nt"&gt;--dry-run&lt;/span&gt;

&lt;span class="c"&gt;# Apply fixes&lt;/span&gt;
fallow fix

&lt;span class="c"&gt;# Non-interactive for CI/agents&lt;/span&gt;
fallow fix &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It only touches things it's confident about. Unused files aren't auto-deleted because that's a bigger decision you should make yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  For the "I already use knip" crowd
&lt;/h2&gt;

&lt;p&gt;Fair question. Knip is a solid tool with 141 plugins and a large community. If it's working for you and speed isn't a pain point, keep using it.&lt;/p&gt;

&lt;p&gt;Reasons you might want fallow instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Speed is a pain point (especially in CI or watch mode)&lt;/li&gt;
&lt;li&gt;You want dead code + duplication detection in one tool (replacing both knip and jscpd)&lt;/li&gt;
&lt;li&gt;You need SARIF output for GitHub Code Scanning&lt;/li&gt;
&lt;li&gt;You want &lt;code&gt;--changed-since&lt;/code&gt; to only check files modified in a PR&lt;/li&gt;
&lt;li&gt;You want inline suppression comments (&lt;code&gt;// fallow-ignore-next-line&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;You want baseline comparison for incremental adoption in existing projects&lt;/li&gt;
&lt;li&gt;You use AI coding agents and want structured JSON output or MCP integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you do switch, &lt;code&gt;fallow migrate&lt;/code&gt; converts your knip config automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI agent integration
&lt;/h2&gt;

&lt;p&gt;This is the use case I care about most. Agents generate code but have no way to know what's unused across the full project. Fallow gives them that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Agent checks its own work after generating code&lt;/span&gt;
fallow check &lt;span class="nt"&gt;--changed-since&lt;/span&gt; main &lt;span class="nt"&gt;--format&lt;/span&gt; json

&lt;span class="c"&gt;# Agent auto-fixes what it can&lt;/span&gt;
fallow fix &lt;span class="nt"&gt;--yes&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's also an MCP server (&lt;code&gt;fallow-mcp&lt;/code&gt;) that exposes 6 tools for typed tool calling: &lt;code&gt;analyze&lt;/code&gt;, &lt;code&gt;check_changed&lt;/code&gt;, &lt;code&gt;find_dupes&lt;/code&gt;, &lt;code&gt;fix_preview&lt;/code&gt;, &lt;code&gt;fix_apply&lt;/code&gt;, and &lt;code&gt;project_info&lt;/code&gt;. Works with Claude Code, Cursor, and any MCP-compatible agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  CI integration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# GitHub Actions&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fallow-rs/fallow@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sarif&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SARIF output uploads to GitHub Code Scanning for inline PR annotations. &lt;code&gt;--changed-since&lt;/code&gt; scopes analysis to PR changes only. &lt;code&gt;--save-baseline&lt;/code&gt; and &lt;code&gt;--baseline&lt;/code&gt; let you adopt incrementally without fixing every existing issue first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx fallow check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No install, no config, no signup. Takes about a second on most projects.&lt;/p&gt;

&lt;p&gt;If you want duplication analysis too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx fallow dupes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/fallow-rs/fallow" rel="noopener noreferrer"&gt;fallow-rs/fallow&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://docs.fallow.tools" rel="noopener noreferrer"&gt;docs.fallow.tools&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;VS Code: &lt;a href="https://marketplace.visualstudio.com/items?itemName=fallow-rs.fallow-vscode" rel="noopener noreferrer"&gt;fallow-rs.fallow-vscode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;MIT licensed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'd love feedback. If fallow finds something useful (or misses something it shouldn't), open an issue or drop a comment here.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>nextjs</category>
      <category>node</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
