fallow dupes finds duplicated code blocks across your entire codebase. jscpd v5 is faster for raw duplication scanning on the current README benchmark fixtures; fallow's advantage is running duplication inside the same audit flow as dead code, dependency, complexity, and architecture checks.
fallow dupes
Most dead-code analysis tools stop at finding unused exports and unreachable files. Fallow goes further: it includes duplication detection in the same binary, using the same module graph. This means you can cross-reference dead code with duplication in a single pass.
When you run fallow dead-code --include-dupes, fallow identifies code blocks that are both duplicated and unused. These are the highest-value cleanup targets: removing them eliminates dead code and reduces duplication simultaneously.
Running duplication analysis alongside dead-code detection also means:
Exact token-for-token clones only. No normalization is applied; the code must be character-identical after tokenization.
fallow dupes --mode strictBest for finding exact copy-paste where nothing was changed.
dupes --mode semantic still compares normalized code structure. To find
complete functions that may share intent despite different structure, use
the separate, opt-in fallow similar-code
workflow. Its model-backed results are always unverified and require review.
Add --near to include function-scoped clones with small structural edits:
fallow dupes --near
Near-miss detection uses semantic token normalization and requires at least 80% shingle similarity between every pair in a group. It runs alongside exact clone detection and remains opt-in because it does more work and can surface code that needs human review. Set duplicates.near to true to enable it by default, or use fallow.duplication.near in VS Code.
Here's what typical output looks like:
● Duplicates (3 clone groups)
57 lines 2 instances
src/components/Calendar/CalendarMonth.stories.tsx:597-653
src/components/Calendar/CalendarYear.stories.tsx:818-874
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
Identical code blocks detected via suffix-array analysis, https://docs.fallow.tools/explanations/duplication#clone-groups
✓ 27,255 lines (19.4%) duplicated across 398 files (0.23s)
In semantic mode, fallow also reports renamed identifiers:
● Duplicates (2 clone groups)
196 lines 2 instances
src/lib/dutch-holidays.ts:193-388
src/lib/dutch-holidays.ts:389-584
Renamed: holidays2024→holidays2025, year2024→year2025
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
Renamed: analyticsData→cashflowData→incomeData
Identical code blocks detected via suffix-array analysis, https://docs.fallow.tools/explanations/duplication#clone-groups
✗ 94,457 lines (67.2%) duplicated across 775 files (3.74s)
fallow dupes --min-tokens 50 # Minimum tokens per clone (default: 50)
fallow dupes --min-lines 5 # Minimum lines per clone (default: 5)
fallow dupes --min-occurrences 3 # Only report clones repeated 3+ times (default: 2)
fallow dupes --threshold 5 # Fail if duplication exceeds 5%
fallow dupes --skip-local # Only cross-directory duplicates
By default fallow reports every duplicated pair (minOccurrences: 2). If you follow the "rule of three" and only want to refactor logic once it appears in three or more places, raise the threshold:
{
"duplicates": {
"minOccurrences": 3
}
}
Values below 2 are rejected, since a single occurrence is not a duplicate. Raising this skips context-sensitive pairs and focuses on widespread copy-paste worth abstracting. The VS Code extension exposes the same control as the fallow.duplication.minOccurrences setting.
Fallow multiplies duplicated token count by occurrence count, then applies a spread boost. Spread is the maximum directory-tree distance between copies. Copies in the same file use one spread step per 250 lines of separation. The boost is capped at 15%, so larger and more repeated clones remain the main priority.
The default report and --top use this order. JSON output includes spread on every clone group, plus similarity for near-miss groups.
Use duplicates.ignoredClones when a specific clone group is intentional but you want other duplication findings to remain active:
{
"duplicates": {
"ignoredClones": ["dup:6f12ab34:2"]
}
}
Each entry combines the group's fingerprint with its instance count. Copy the fingerprint from the human listing or clone_groups[].fingerprint in JSON, then append :<instance_count>. A token change or a new copy makes the group reportable again. Formatting-only edits keep the same fingerprint.
JSON output sets stats.clone_groups_ignored when reviewed groups are hidden. Near-miss runs set stats.near_candidates_skipped if bounded-work limits skip candidate comparisons, which means the near-miss result may be incomplete.
Ordinary dup:<8hex> handles and widened dup:<16hex> handles are unchanged. When groups share a full content fingerprint, their handles use dup:<16hex>-rN. These collision handles stay the same when an identical report is produced from another checkout location, but can change when the colliding groups change.
Older numeric collision handles such as dup:<16hex>-1 still load in ignoredClones, but no longer hide or select a group. A baseline containing those handles also lets the affected findings reappear. Run fallow dupes again, review each affected group, then copy its reported handle into your configuration or refresh the baseline. Do not mechanically replace -1 with -r1: the ordinal can refer to a different group.
Upgrade every runner that reads a shared configuration before adding -rN keys to ignoredClones. Older binaries reject that syntax.
Clone groups sharing the same file set are grouped into clone families with refactoring suggestions:
Compare TypeScript and JavaScript files by stripping type annotations:
fallow dupes --cross-language
Fallow normalizes .ts files to their .js equivalent for comparison. This catches clones where one copy was converted from TypeScript to JavaScript or vice versa.
Files with the same module wiring are a structural property of well-formatted code, not copy-paste, so that wiring is stripped from the token stream by default. This covers ES imports, re-export declarations, and top-level static require() binding declarations.
To count module wiring as clone candidates again, opt out on the command line:
fallow dupes --no-ignore-imports
Or set it permanently in config:
{
"duplicates": {
"ignoreImports": false
}
}
Runtime code, local exports, side-effect require() calls, nested require() calls, dynamic require arguments, and mixed declarations are still counted.
Only check duplication in files changed since a git ref:
fallow dupes --changed-since main
Useful in CI to only report new duplication introduced in a pull request.
Adopt duplication limits incrementally:
# Save current duplication as baseline
fallow dupes --save-baseline fallow-baselines/dupes.json
# Fail only on new duplication
fallow dupes --baseline fallow-baselines/dupes.json
Trace all clones of a specific code location:
fallow dupes --trace src/utils.ts:42
Cold runs (no cache) so each tool works from scratch. Fastest tool per row in bold.
| Project | Files | fallow | jscpd | Faster |
|---|---|---|---|---|
| astro | 2,859 | 549ms | 189ms | jscpd 2.9x |
| fastify | 286 | 90ms | 64ms | jscpd 1.4x |
| next.js | 20,552 | 12.66s | 861ms | jscpd 14.7x |
| preact | 244 | 58ms | 49ms | jscpd 1.2x |
| TanStack/query | 901 | 133ms | 96ms | jscpd 1.4x |
| svelte | 3,337 | 317ms | 172ms | jscpd 1.8x |
| TypeScript | 38,146 | 13.45s | 4.58s | jscpd 2.9x |
| vite | 1,420 | 174ms | 74ms | jscpd 2.3x |
| vue/core | 522 | 109ms | 78ms | jscpd 1.4x |
| zod | 174 | 54ms | 53ms | jscpd 1.0x |
jscpd's Rust rewrite (v5) is faster than fallow for raw duplication scanning across these projects. Fallow's advantage is running duplication inside the same single audit pass as dead code, dependency, complexity, CSS, framework, and security checks, not raw scan speed.
Fallow uses a suffix array with LCP for clone detection, avoiding quadratic pairwise comparison.