Code duplication
Find copy-pasted code in your TypeScript and JavaScript codebase and see which copies to extract first. Runs in the same binary and config as dead-code, dependency, complexity, and architecture analysis.
fallow dupes finds duplicated code blocks in your whole codebase and ranks them, so you know which copies to extract first. It runs in the same audit as the dead code, dependency, complexity, and architecture checks. For raw duplication scans, jscpd v5 is faster on the current README benchmark fixtures.
fallow dupes
Why built-in duplication matters
Most dead-code analysis tools find only unused exports and unreachable files. Fallow also detects duplication in the same binary, with the same module graph. You can therefore compare dead code with duplication in one pass.
fallow dead-code --include-dupes finds code blocks that are duplicated and unused. These are the best blocks to clean up first. When you delete one, you remove dead code and duplication in one change.
One run for duplication and dead code also gives you:
- One tool, one config, one CI step. You do not install and configure a separate duplication detector.
- Shared file discovery. The same ignore patterns, entry points, and workspace config apply to both analyses.
- Combined findings. Fallow reports clone families in unused files as combined findings.
- The same output formats. JSON, SARIF, markdown, compact, and CodeClimate output work the same way for duplication and for dead code.
Detection modes
Only exact token-for-token clones. Fallow applies no normalization. The code must be character-identical after tokenization.
fallow dupes --mode strictUse it to find exact copies with no changes.
The default mode. Strict and mild modes give identical results, because AST-based tokenization already removes whitespace and comments before the comparison.
fallow dupes --mode mildStart with this mode for general detection.
Matches clones also when string literal values differ. Variable names must still match.
fallow dupes --mode weakUse it to find copied code where only the strings (URLs, messages, labels) changed.
Finds clones with renamed variables and different literal values. Token-type normalization matches code with the same structure.
fallow dupes --mode semanticThe broadest mode. Finds code that someone copied and then changed with new variable names and values.
dupes --mode semantic still compares normalized code structure. To find
complete functions that can have the same intent with a different structure,
use the separate, opt-in fallow similar-code
workflow. Its model-backed results are always unverified, and you must review them.
Near-miss clones
To also report function-scoped clones with small structural edits, add --near:
fallow dupes --near
Near-miss detection uses semantic token normalization. Every pair in a group must have at least 80% shingle similarity. It runs next to exact clone detection. It is opt-in, because it does more work and can report code that a person must review. To turn it on by default, set duplicates.near to true, or use fallow.duplication.near in VS Code.
Typical output:
note: module wiring excluded from clone detection (--no-ignore-imports to include it)
● Duplicates (2 clone groups)
57 lines 2 instances dup:3f9a1c2e
src/components/Calendar/CalendarMonth.stories.tsx:597-653
src/components/Calendar/CalendarYear.stories.tsx:818-874
42 lines 3 instances dup:8b41d0a7
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
Duplicate code blocks
https://docs.fallow.tools/explanations/duplication#clone-groups
✗ 27,255 lines (19.4%) duplicated across 398 files (0.23s)
Each group header shows the line count, the instance count, the spread (when it is above 0), and the dup: handle of the group.
In semantic mode, fallow also matches copies with renamed identifiers and changed literal values. The output has the same shape. It does not list the renamed identifiers:
note: module wiring excluded from clone detection (--no-ignore-imports to include it)
● Duplicates (2 clone groups)
196 lines 2 instances spread 1 dup:c4e07b19
src/lib/dutch-holidays.ts:193-388
src/lib/dutch-holidays.ts:401-596
42 lines 3 instances dup:8b41d0a7
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
Duplicate code blocks
https://docs.fallow.tools/explanations/duplication#clone-groups
✗ 94,457 lines (67.2%) duplicated across 775 files (3.74s)
Thresholds and limits
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 --fail-on-issues # Fail if any clone group remains
fallow dupes --skip-local # Only cross-directory duplicates
Minimum occurrences
By default, fallow reports every duplicated pair (minOccurrences: 2). If you follow the "rule of three" and refactor logic only when it appears in three or more places, raise the threshold:
{
"duplicates": {
"minOccurrences": 3
}
}
Fallow rejects values below 2, because a single occurrence is not a duplicate. A higher value skips pairs that can depend on context, and keeps the copies that are worth an abstraction. The VS Code extension has the same control as the fallow.duplication.minOccurrences setting.
Spread-aware ranking
Fallow ranks clone groups so the most expensive copies come first. It multiplies the duplicated token count by the occurrence count, then adds a spread boost. Spread is the maximum directory-tree distance between copies. For copies in the same file, each 250 lines between them count as one spread step. The boost is at most 15%, so larger and more repeated clones stay the main priority.
The default report and --top use this order. The JSON output has spread on every clone group, and similarity on near-miss groups.
Reviewed clone groups
When a clone group is intentional, add it to duplicates.ignoredClones. Fallow still reports all other duplication findings:
{
"duplicates": {
"ignoredClones": ["dup:6f12ab34:2"]
}
}
Each entry is the fingerprint of the group plus its instance count. Copy the fingerprint from the human listing or from clone_groups[].fingerprint in JSON, then add :<instance_count>. After a token change or a new copy, fallow reports the group again. An edit that changes only formatting keeps the same fingerprint.
When fallow hides reviewed groups, the JSON output sets stats.clone_groups_ignored. When work limits skip candidate comparisons in a near-miss run, fallow sets stats.near_candidates_skipped. The near-miss result can then be incomplete.
Migrating collision handles
Ordinary dup:<8hex> handles and widened dup:<16hex> handles do not change. When groups have the same full content fingerprint, their handles use dup:<16hex>-rN. A collision handle stays the same when you produce an identical report from another checkout location. It can change when the colliding groups change.
Older numeric collision handles such as dup:<16hex>-1 still load in ignoredClones, but they no longer hide or select a group. With a baseline that has these handles, the affected findings also appear again. To migrate:
- Run
fallow dupesagain. - Review each affected group.
- Copy its reported handle into your configuration, or refresh the baseline.
Do not replace -1 with -r1 without a review. The number can point to a different group.
Before you add -rN keys to ignoredClones, upgrade every runner that reads a shared configuration. Older binaries reject that syntax.
Clone families
Fallow puts clone groups with the same set of files into a clone family and suggests a refactoring:
- Extract function: the clones are in the same file.
- Extract module: the clones are in more than one file.
Cross-language detection
To compare TypeScript and JavaScript files, pass --cross-language. Fallow removes type annotations before it compares:
fallow dupes --cross-language
Fallow converts .ts files to their .js equivalent for the comparison. This finds clones where someone converted one copy from TypeScript to JavaScript, or the reverse.
Ignoring imports
Two files with the same imports are not copy-paste. By default, fallow removes this module wiring from the token stream. This covers ES imports, re-export declarations, and top-level static require() binding declarations.
To count module wiring as clone candidates again, use the command-line flag:
fallow dupes --no-ignore-imports
Or set it in the config:
{
"duplicates": {
"ignoreImports": false
}
}
Fallow still counts these:
- runtime code
- local exports
- side-effect
require()calls - nested
require()calls - dynamic require arguments
- mixed declarations
Symlinked files
A symlinked file or directory in the project gives a clone of the same code under two paths. This is not copy-paste. Fallow marks such an instance with is_symlink: true in JSON output and (symlink) in human output. To report only duplication between real files, use the command-line flag:
fallow dupes --ignore-symlinks
Or set it in the config:
{
"duplicates": {
"ignoreSymlinks": true
}
}
The duplication statistics then count only real files. See Symlinked files for the details.
Incremental analysis
To check duplication only in the files that changed since a git ref, pass --changed-since:
fallow dupes --changed-since main
In CI, this reports only the duplication that a pull request adds.
Baseline comparison
To add duplication limits to an existing codebase, save the current duplication as a baseline. Fallow then fails only on new duplication:
# 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 --fail-on-issues
Without --fail-on-issues (or --ci), the run reports the new clone groups and exits 0. See exit codes.
Debugging
To see all clones of one code location, trace it:
fallow dupes --trace src/utils.ts:42
Performance
All runs are cold (no cache), with fallow 2.100.0 on an Apple M5. Warm runs are faster again.
| Project | Files | Time |
|---|---|---|
| astro | 2,859 | 549ms |
| fastify | 286 | 90ms |
| next.js | 20,552 | 12.66s |
| preact | 244 | 58ms |
| TanStack/query | 901 | 133ms |
| svelte | 3,337 | 317ms |
| TypeScript | 38,146 | 13.45s |
| vite | 1,420 | 174ms |
| vue/core | 522 | 109ms |
| zod | 174 | 54ms |
Fallow runs duplication in the same audit pass as the dead code, dependency, complexity, CSS, framework, and security checks.
Fallow finds clones with a suffix array and an LCP array. This avoids a quadratic comparison of each pair.