TL;DR: fallow is Rust-native codebase intelligence for TypeScript and JavaScript. The free static layer finds unused code (files, exports, types, enum and class members, dependencies), code duplication, circular dependencies, complexity hotspots, and architecture boundary violations across your module graph. Zero config, 95 framework plugins auto-detected, sub-second on most projects. An optional paid runtime layer (Fallow Runtime) adds production execution evidence on top. npx fallow to try it now.
The problem that kept bugging me
Every codebase I've worked on has structural drift. Exports that nothing imports. Dependencies in package.json 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. Modules that import each other in cycles. Functions that have quietly become complexity hotspots. Boundaries that were supposed to separate features but have leaked across the codebase.
That drift 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. The cycles make builds fragile. The complexity hotspots accumulate in the busiest paths. And the longer nobody touches any of it, the scarier it gets to change.
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, whether a module is in a cycle, or whether a function has tipped over a complexity threshold requires building a complete model of the codebase across every file. That is a graph problem, not a "read the file and figure it out" problem.
Existing tools work, but they were slow enough that I'd only run them occasionally. So I built fallow.
What it does
Fallow builds the full import/export graph of your project and reports across five analysis areas:
- Unused code: files, exports, types, enum members, class members, dependencies, devDependencies, unresolved imports, unlisted dependencies, duplicate exports, unused pnpm catalog entries
- Code duplication: suffix-array clone detection in four modes (strict, mild, weak, semantic), with clone families that suggest whether to extract a function or a module
- Circular dependencies: cycle detection across the full module graph, with no depth limits
- Complexity hotspots: cyclomatic and cognitive complexity per function, refactoring targets, and per-file complexity-density rankings
-
Architecture boundaries: zone definitions with
allowlists, presets (Layered, Hexagonal, FeatureSliced, Bulletproof),autoDiscoverparent zones, and a per-ruleallowTypeOnlyescape hatch for type-only imports across zones
Here's what it looks like:
$ fallow
● 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
● Clone groups (1,184)
... 19.4% duplication across 398 files
● Complexity hotspots (12)
... cyclomatic 28 in src/features/forecasting/server/procedures/analytics.ts
Found 401 issues in 0.16s
That last line is the part I'm most proud of. 0.16 seconds for 401 issues across a real-world codebase.
An optional paid runtime layer (Fallow Runtime) ingests production V8 coverage and joins it back to the static graph so the same report can flag hot-path changes, surface cold paths with deletion confidence, and retire stale flags with proof.
Why Rust
I didn't pick Rust to be trendy. The bottleneck in cross-file analysis is parsing every file in the project to build the module graph. Fallow uses the Oxc parser natively in Rust (not through NAPI bindings) and rayon for parallel file processing across all cores. If you're already looking at oxlint or oxfmt, fallow is the same family. Linters check files. TypeScript checks types. Fallow checks the codebase.
Here is how that plays out on real projects:
| Project | Files | fallow |
|---|---|---|
| zod | 174 | 19ms |
| preact | 244 | 20ms |
| fastify | 286 | 24ms |
| vue/core | 522 | 63ms |
| TanStack/query | 901 | 148ms |
| svelte | 3,337 | 325ms |
| next.js | 20,416 | 1.48s |
Sub-second on every project up to 3,000+ files, and 1.5 seconds on a 20,000-file Next.js monorepo. Memory usage stays in the low hundreds of MB even on the big ones, which matters in CI. The dedicated comparison page has the full matrix against existing Node-based tooling if you want the side-by-side.
Duplication detection built in
Most projects I've worked on also have a copy-paste problem. fallow has a dupes command that finds duplicated code blocks using a suffix array algorithm (no quadratic pairwise comparison):
$ fallow dupes
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)
Four detection modes: strict (exact), mild (default), weak (different string literals), and semantic (catches renamed variables too). Clone families group related duplicates and suggest whether to extract a function or a module.
Complexity, hotspots, and architecture boundaries
fallow health computes cyclomatic and cognitive complexity per function, surfaces complexity hotspots, ranks refactoring targets, and reports vital signs (duplicated lines, files scored, total deps). fallow list --boundaries and the boundary rules in .fallowrc.json enforce architecture zones so a feature module can't import from a sibling feature unless an allow rule says so. Presets cover Layered, Hexagonal, Feature-Sliced, and Bulletproof out of the box; autoDiscover expands a parent zone into one child per immediate subdirectory automatically.
Zero config, 95 plugins
Fallow reads your package.json and auto-detects your framework stack. Next.js, Vite, Vitest, Playwright, Storybook, ESLint, Tailwind, Prisma, Drizzle, Turborepo, Nx, Angular, NestJS, AdonisJS, Expo Router, and more. 95 plugins total, with deep config-file parsing for the ones that matter (reading next.config.js, vite.config.ts, astro.config.mjs, etc. to understand entry points and aliases).
No .fallowrc.json required unless you want to customize. Just run it.
Auto-fix
Fallow can remove unused exports, unused dependencies, and unused enum members automatically:
# Preview what would change
fallow fix --dry-run
# Apply fixes
fallow fix
# Non-interactive for CI/agents
fallow fix --yes
It only touches things it's confident about. Unused files aren't auto-deleted because that's a bigger decision you should make yourself.
Why fallow over what you have today
A few reasons fallow might be a better fit:
- Speed is a pain point (especially in CI or watch mode)
- You want unused code, duplication, circular deps, complexity hotspots, and boundary checks in one tool instead of four
- You need SARIF output for GitHub Code Scanning
- You want
--diff-fileto scope every finding to lines changed in a PR - You want inline suppression comments (
// fallow-ignore-next-line) - You want baseline comparison for incremental adoption in existing projects
- You use AI coding agents and want structured JSON output or MCP integration
- You plan to eventually layer in production runtime evidence (Fallow Runtime)
fallow migrate converts existing tool configs into a fallow config automatically.
AI agent integration
This is the use case I care about most. Agents generate code but have no way to know what's unused, duplicated, or structurally drifted across the full project. Fallow gives them that:
# Agent checks its own work after generating code
fallow audit --diff-file pr.diff --format json
# Agent auto-fixes what it can
fallow fix --yes --format json
There is also an MCP server (fallow-mcp) that exposes typed tools (analyze, check_changed, find_dupes, check_health, fix_preview, fix_apply, project_info) so agents can call them directly. Works with Claude Code, Cursor, Codex, Windsurf, Gemini CLI, and any MCP-compatible agent.
CI integration
# GitHub Actions
- uses: fallow-rs/fallow@v2
with:
format: sarif
SARIF output uploads to GitHub Code Scanning for inline PR annotations. --diff-file scopes every finding to lines inside an added hunk. --save-baseline and --baseline let you adopt incrementally without fixing every existing issue first. A GitLab CI template is bundled too.
Try it
npx fallow
No install, no config, no signup. Takes about a second on most projects, and you get unused code, duplication, circular deps, complexity hotspots, and boundary violations in one pass.
If you want a single category:
npx fallow dead-code # unused-code rules only
npx fallow dupes # duplication only
npx fallow health # complexity + hotspots + refactoring targets
npx fallow list --boundaries # architecture zones
- GitHub: fallow-rs/fallow
- Docs: docs.fallow.tools
- VS Code: fallow-rs.fallow-vscode
- MIT licensed
I'd love feedback. If fallow finds something useful (or misses something it shouldn't), open an issue or drop a comment here.
Top comments (0)