Skip to content
Fallow home
All docs pages

Adopt Fallow in an existing repo

Clean up the backlog in an existing TypeScript or JavaScript codebase under one team policy, then use fallow audit to keep new changes clean. An AI agent can do the cleanup work.

Use this guide when your repository already has a backlog: unused code, duplicates, complexity hotspots, and existing exceptions. You clean the backlog once, then fallow keeps new changes clean.

At the end, you have:

  • a repo-level fallow policy in config
  • dead code fixed, or modeled as intentional
  • duplication at or below your chosen threshold
  • complex functions refactored, or thresholds widened on purpose with a written justification
  • fallow audit that enforces the same policy on changed files

Repo clean vs PR clean

These are two different goals. You need both, in this order.

Repo clean (this guide)

Analyze the full repo to understand and clean the whole codebase. Use fallow, fallow dead-code, fallow dupes, and fallow health.

PR clean

Enforce the policy on the changed files of every PR. Use fallow audit after the repo is clean.

Before you start

1. First run: get the whole picture

From your project root, run:

npx fallow

The quick start shows the focused commands for the cleanup (dead-code, dupes, health). It also shows how fallow init writes a starting config.

2. Decide the project policy before triage

Do not start by suppressing findings one by one. That gives you a list of inline exceptions that only grows, and no clear policy.

If you do not own the policy, run section 3 on a branch first. Then bring the findings to the policy discussion. The fallow output makes the team decide what it actually cares about.

First, decide as a team:

  • what counts as a real entry point (workers, scripts, route files, dynamically loaded modules)
  • which files are generated or out of scope
  • which packages are public APIs that ship exports outside the repo
  • which dependencies are runtime-provided or intentionally retained
  • which health thresholds you actually want to enforce
  • whether duplication should warn or fail

This config is a good start:

{
  "$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json",
  "ignorePatterns": ["**/*.generated.ts", "**/*.d.ts"],
  "rules": {
    "unresolved-imports": "error",
    "unlisted-dependencies": "error",
    "unused-exports": "warn"
  }
}

Rules that you do not list keep their defaults. The health thresholds default to maxCyclomatic: 20 and maxCognitive: 15. Override them in a health block only when the realistic baseline of your repo is different.

During rollout, use warn to see findings without blocking CI. Change rules to error as the backlog gets smaller. The rules reference lists every issue type and severity.

3. Work findings in the right order

Use this order for most existing repos. Each step removes noise that would make the next step harder to read.

  1. Fix unresolved imports and unlisted dependencies

    These findings are high-confidence and rarely need discussion. They are real bugs that have not failed yet, so fix them first.

    npx fallow dead-code --unresolved-imports
    npx fallow dead-code --unlisted-deps
  2. Delete unused files

    Unreachable files are usually safe to delete. If you are not sure about a file, run fallow list --entry-points to check reachability before you delete it.

    npx fallow dead-code --unused-files
    npx fallow list --entry-points
  3. Remove unused dependencies

    Remove unused packages early. This gives less noise in later steps and faster installs.

    npx fallow dead-code --unused-deps
  4. Clean up unused exports, types, enum members, and class members

    This category is usually the biggest. Fix real dead code. For an intentional public API, use visibility tags or config, and do not suppress it. To give this step to an agent, use the prompt in section 8.

    npx fallow dead-code --unused-exports --unused-types

    To focus on enum members and class members, add --unused-enum-members and --unused-class-members. In a TypeScript project, add --type-aware when aliases, re-exports, class contracts, packages, or exact consumers affect the decision. This flag starts an optional semantic pass that is slower. See Type-aware TypeScript analysis and the fallow dead-code reference.

  5. Consolidate duplication

    Merge repeated logic into shared helpers. Ignore generated or template-heavy files only when the duplication is intentional.

    npx fallow dupes
    npx fallow dupes --mode semantic   # also catch renamed-variable clones
  6. Split health hotspots

    The fallow health output has Hotspots and Targets sections that rank the worst files. Start with those. Do not debate every file above the mean.

    npx fallow health

4. Match the reason to the right mechanism

Inline suppression is not the default. For each finding you keep, pick the mechanism that matches the reason.

External or public API

These are exports that code outside the repo uses (published libraries, SDKs, public packages).

Prefer, in order:

  • @public, @internal, @beta, @alpha JSDoc visibility tags on the export
  • publicPackages to mark a whole package as external API
  • ignoreExports only when no other mechanism can express a file-level or export-level exception

Use publicPackages and visibility tags together. publicPackages marks the package as used from outside the repo. The @public and @internal tags separate the public API from internal helpers that you export only for use in other files.

Framework or runtime-discovered entry points

These are files that your framework or runtime calls, but that no other module imports directly (workers, CLI scripts, route files, plugin modules).

Prefer, in order:

  • built-in plugin detection, which handles Next.js, Vite, NestJS, Remotion, and dozens more with no config
  • entry for project-specific entry globs
  • dynamicallyLoaded for code pulled in through reflection, manifests, or dynamic imports
  • fallow list --entry-points to see what fallow already considers reachable

Generated or vendored files

These are files from a build step, a code generator, or a vendored third-party bundle.

Prefer:

  • ignorePatterns to exclude them from analysis entirely
  • health.ignore when the noise is only in health findings and dead-code analysis must still run
  • duplication configuration for clone-heavy generated code

Runtime-provided or intentionally retained dependencies

These are packages that only the runtime uses (no static import), CLI tools, or peer dependencies.

Prefer:

One-off intentional unused export

This is an export that you keep on purpose (compatibility shims, future API).

Prefer:

  • /** @expected-unused */ on the export

fallow tells you when an @expected-unused tag is no longer needed. When a module imports the export, fallow reports the tag as a stale suppression, and you can remove it. Fallow reports stale fallow-ignore comments in the same way. Use the tag freely for shims and future API.

One-off false positive

This is one location where fallow is wrong, and no config rule expresses the reason clearly.

Prefer:

  • // fallow-ignore-next-line <issue> on the line above
  • // fallow-ignore-file <issue> only when the whole file is truly exceptional

Repo-wide policy change

The project has a different standard for a whole category.

Prefer:

  • rules, health, duplication thresholds, or overrides blocks

Do this only for a real team policy. Do not do it to hide a few bad hotspots.

Suppression has the full decision tree and examples. The configuration overview lists every key.

5. Keep exceptions narrow and reviewable

A narrow exception documents one decision. A broad exception hides findings you have not reviewed.

Good exceptions:

  • a single @public export on a library entry point
  • one ignoreDependencies entry for a runtime-provided package
  • one entry pattern for worker scripts
  • one file-level generated-code ignore with a comment explaining why

Bad exceptions:

  • broad ignorePatterns that silently exclude half the repo
  • repeated inline suppressions that could be one config rule
  • a higher global health.maxCyclomatic to hide a few hotspots (acceptable only when the new threshold is a considered project standard with a written justification)

If you cannot explain an exception in one sentence, you probably use the wrong mechanism.

6. Define done

Work in two stages. Most teams turn on fallow audit at the end of stage 1, and finish stage 2 in the weeks after.

Stage 1: good enough to ship fallow audit

  • the config holds the chosen policy, and scattered suppressions do not
  • no unresolved imports or unlisted dependencies are left
  • obvious dead code (unused files, unused dependencies) is removed
  • fallow audit runs in CI and passes for new changes against the default branch

Stage 2: ideal state

  • no functions above your chosen health thresholds, because you refactored them or widened the threshold on purpose with a written justification
  • duplication at or below the chosen threshold
  • stale suppressions removed, or accepted on purpose

7. Turn on PR enforcement

When stage 1 is done, add the PR gate:

npx fallow audit

fallow audit runs dead code, duplication, complexity, and styling analysis on the changed files only. It then returns a pass, warn, or fail verdict. The fallow audit reference has the flags and CI recipes.

Pick one rollout strategy

This option is best for smaller teams. CI never blocks. The team fixes findings because everyone sees the warnings.

{
  "rules": {
    "unused-exports": "warn",
    "unused-files": "warn",
    "unused-dependencies": "warn"
  }
}

The risk: a gate that only warns can stay that way forever. Set a calendar reminder to change rules to error after the first clean month.

You can also set the baselines in .fallowrc.json and run fallow audit with no flags:

{
  "audit": {
    "deadCodeBaseline": "fallow-baselines/dead-code.json",
    "healthBaseline":   "fallow-baselines/health.json",
    "dupesBaseline":    "fallow-baselines/dupes.json"
  }
}

A baseline records debt that you still must pay. If the baseline file only grows, nobody enforces the policy.

8. Clean up with an agent

Fallow finds the problems, and an AI agent can fix them. Use Claude Code, Cursor, Codex, Windsurf, or any coding assistant that can run shell commands. The edits are mechanical. The decisions ("delete this export", "mark it @public", or "add it to entry") need judgement that reads the code.

Pick the integration level that your agent supports:

Copy-paste adoption prompt (for agents without skills or MCP)

Paste this prompt into your agent. The prompt is complete, so the agent does not need to fetch this page.

Adopt Fallow in this repository.

Goal:
- use full-repo analysis first (`fallow`, `fallow dead-code`, `fallow dupes`, `fallow health`), not `fallow audit`
- fix real dead code, duplication, and complexity issues in code
- model intentional exceptions with the narrowest correct mechanism
- end with no functions above the repo's chosen health thresholds, or a consciously widened threshold with written justification
- then set up `fallow audit` as a PR gate

Process:
1. Run `npx fallow`, then `npx fallow dead-code`, `npx fallow dupes`, and `npx fallow health`. Use `--format json` if you want structured output.
2. If no config exists, run `fallow init` and create a minimal repo policy.
3. Fix high-confidence issues first:
   - unresolved imports
   - unlisted dependencies
   - unused files (sanity-check with `fallow list --entry-points`)
   - unused dependencies
4. For each remaining finding, choose one path:
   - fix it in code (preferred)
   - model it in config
   - add a narrow inline exception only if it is truly one-off
5. Match reasons to mechanisms:
   - external API: `@public` / `@internal` / `@beta` / `@alpha`, or `publicPackages`
   - runtime or framework entry point: `entry`, `dynamicallyLoaded`, plugin-aware config
   - generated code: `ignorePatterns`, `health.ignore`
   - intentionally retained dependency: `ignoreDependencies`
   - intentional unused export: `@expected-unused` (fallow reports it as stale when the export becomes used)
   - one-off false positive: `fallow-ignore-next-line`
   - repo-wide policy: `rules`, `health`, duplication settings, `overrides`
6. Prefer config-level modeling over repeated suppression.
7. Keep every exception narrow and explain why it exists in a commit message.
8. Re-run Fallow after each batch until the repo is clean under the chosen policy.
9. Only after repo cleanup, run `npx fallow audit` and wire it into CI.

At the end, report:
- code changes
- config changes
- exceptions added and why
- anything left
- the final commands and outputs that show the repo is clean

See also