Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.fallow.tools/llms.txt

Use this file to discover all available pages before exploring further.

fallow dead-code enforces architecture boundaries by checking that imports between directories follow your rules. Define and declare which zones may import from which.
fallow dead-code --boundary-violations
Boundary violations are included in fallow dead-code output by default. Use --boundary-violations to show only boundary issues.

Quick start with presets

The fastest way to add boundaries is with a built-in preset. Fallow ships four presets for common architecture patterns:
{
  "boundaries": {
    "preset": "bulletproof"
  }
}
Run fallow list --boundaries to see the expanded zones and rules:
$ fallow list --boundaries
Boundaries: 5 zones, 5 rules

Zones:
  app                  3 files  src/app/**
  features/auth        7 files  src/features/auth/**
  features/billing     5 files  src/features/billing/**
  shared               8 files  src/components/**, src/hooks/**, src/lib/**, ...
  server               4 files  src/server/**

Rules:
  app features/auth, features/billing, shared, server
  features/auth shared, server
  features/billing shared, server
  server shared
  shared               (isolated  no imports allowed)

Presets

A widely used React/Next.js pattern. Feature modules are isolated from each other; shared utilities and server infrastructure form the base layers.4 logical zones: app, features, shared, server. The features zone auto-discovers immediate child directories so sibling features are isolated as features/<name> zones.
app              → every feature, shared, server
features/<name>  → shared, server
server           → shared
shared           (isolated)
The shared zone covers: components, hooks, lib, utils, utilities, providers, shared, types, styles, i18n.
This preset matches the architecture from Bulletproof React and is the most common pattern in modern React and Next.js projects.

Source root detection

Preset zone patterns use {rootDir}/{zone}/**. Fallow auto-detects the source root from tsconfig.json:
tsconfig.json
{
  "compilerOptions": {
    "rootDir": "./lib"  // zones become lib/app/**, lib/features/**, etc.
  }
}
If no rootDir is found, fallow falls back to src.

Custom zones and rules

For full control, define zones and rules directly:
{
  "boundaries": {
    "zones": [
      { "name": "ui", "patterns": ["src/components/**", "src/pages/**"] },
      { "name": "data", "patterns": ["src/db/**", "src/api/**"] },
      { "name": "shared", "patterns": ["src/lib/**", "src/utils/**"] }
    ],
    "rules": [
      { "from": "ui", "allow": ["shared"] },
      { "from": "data", "allow": ["shared"] },
      { "from": "shared", "allow": [] }
    ]
  }
}

How zones work

  • A file belongs to the first zone whose pattern matches (first-match wins)
  • Files that don’t match any zone are unrestricted: they can import from and be imported by any zone
  • Self-imports are always allowed (files in the same zone can freely import each other)
  • A zone with no rule entry is unrestricted: it can import from any zone
  • A zone with a rule and an empty allow list is isolated: it cannot import from other zones

Auto-discovered feature zones

Use autoDiscover when one logical zone should create one concrete zone per child directory. This is useful for feature-module architectures where src/features/auth and src/features/billing should be isolated from each other without writing a zone and rule for every feature.
{
  "boundaries": {
    "zones": [
      { "name": "app", "patterns": ["src/app/**"] },
      { "name": "features", "autoDiscover": ["src/features"] },
      { "name": "shared", "patterns": ["src/shared/**"] }
    ],
    "rules": [
      { "from": "app", "allow": ["features", "shared"] },
      { "from": "features", "allow": ["shared"] }
    ]
  }
}
If src/features/auth and src/features/billing exist, fallow expands this to features/auth and features/billing. Rules that reference the logical features parent apply to every discovered feature. Explicit child rules, such as from: "features/auth", override generated parent rules.

Subtree scope (root)

Monorepos with per-package boundaries usually have the same internal directory layout under each package (packages/app/src/, packages/core/src/, …). Writing flat patterns from the project root forces zone definitions to scale with the cross product of (zones, packages):
// Without `root`: one zone per (layer, package) pair.
{ "name": "ui-app",      "patterns": ["packages/app/src/**"] },
{ "name": "domain-core", "patterns": ["packages/core/src/**"] }
Set root on a zone to scope its patterns to a subtree. At classification time, fallow checks that the file’s path starts with the root prefix and strips that prefix before matching the patterns against the remainder. Files outside the subtree never match the zone.
{
  "boundaries": {
    "zones": [
      { "name": "ui",     "patterns": ["src/**"], "root": "packages/app/" },
      { "name": "domain", "patterns": ["src/**"], "root": "packages/core/" }
    ],
    "rules": [
      { "from": "ui", "allow": [] }
    ]
  }
}
In this example, packages/app/src/login.tsx classifies as ui and packages/core/src/order.ts classifies as domain. Adding packages/billing/ later only requires another zone entry with the same patterns: ["src/**"] and a different root. Trailing slashes and a leading ./ are normalized for you, so "packages/app", "packages/app/", and "./packages/app/" are equivalent. Backslashes are converted to forward slashes.
Patterns must NOT redundantly include the root prefix. root: "packages/app/" paired with patterns: ["packages/app/src/**"] is rejected at config-resolve time with FALLOW-BOUNDARY-ROOT-REDUNDANT-PREFIX, because patterns are already resolved relative to the root. Drop the root prefix from the pattern (patterns: ["src/**"]).

Overriding preset zones

Start from a preset and customize specific zones or rules. Zones with the same name replace the preset zone; rules with the same from replace the preset rule.
{
  "boundaries": {
    "preset": "hexagonal",
    "zones": [
      { "name": "domain", "patterns": ["src/core/**"] }
    ]
  }
}
This keeps adapters and ports from the hexagonal preset but replaces the domain zone pattern with src/core/**.

Suppressing violations

Suppress individual findings with inline comments:
// fallow-ignore-next-line boundary-violation
import { db } from '../data/client';
Or suppress all boundary violations in a file:
// fallow-ignore-file boundary-violation
To suppress boundary checking entirely, set the rule severity to off:
{
  "rules": {
    "boundary-violation": "off"
  }
}
When introducing boundaries to an existing codebase, start with "warn" severity. Fix violations incrementally, then switch to "error" once the codebase is clean.

Output formats

Boundary violations appear in all output formats:
$ fallow dead-code --boundary-violations
Boundary violations (2)

  src/features/auth/login.ts:3 src/features/billing/api.ts (features  features)
  src/components/Button.tsx:1 src/server/db/client.ts (shared  server)

Inspecting your boundaries

Use fallow list --boundaries to verify your configuration before running analysis:
# Human-readable summary
fallow list --boundaries

# JSON for scripting or MCP tools
fallow list --boundaries --format json --quiet
Fallow warns when a zone matches zero files. This usually means the glob pattern doesn’t match your directory structure. Run fallow list --boundaries to check file counts per zone.

See also

fallow dead-code

CLI reference for the dead code command, including all issue type filters.

Rules & Severity

Set boundary violations to error, warn, or off.

Inline Suppression

Suppress individual findings in source code.