Impact Analysis Technique

Technique

Before refactoring a function, renaming an export, or changing a data shape, you want to know: what breaks? Grep catches direct references, but misses re-exports, dynamic imports, and changes that cascade through data flow. This technique does a structured impact analysis — tracing the full dependency chain from target to leaf nodes.

Run this before the Refactor Planning prompt. Impact analysis answers "what's affected?", refactor planning answers "how do I change it safely?"

The prompt

Analyze the blast radius of changing [file, function, or type]. I want to understand everything that would be affected before I make this change.

## Analysis steps

1. **Direct dependents**
   Find every file that imports from the target file. For each import, note:
   - What is imported (named export, default, type)
   - How it's used (called, rendered, passed as prop, re-exported)

2. **Transitive dependents**
   For each direct dependent, check if it re-exports or passes through the target's interface. Trace the chain until you reach leaf nodes (pages, entry points, test files).

3. **Data flow impact**
   If the change affects a data shape (object structure, function signature, return type):
   - Trace where that data flows — through props, context, API responses, state
   - Identify every consumer that assumes the current shape

4. **Test coverage**
   Which test files directly test the target? Which tests would break due to transitive changes? Are there gaps — dependents with no test coverage that would silently break?

5. **Build & config impact**
   Does the target appear in any config files, build scripts, or CI pipelines? (e.g., referenced in next.config.js, webpack aliases, barrel exports in index files)

## Output format

### Direct dependents ([count] files)
| File | Import | Usage |
|------|--------|-------|
| ... | ... | ... |

### Transitive dependents ([count] files)
| File | Path from target | Usage |
|------|-----------------|-------|
| ... | target → A → B | ... |

### Data flow
[Describe the shape that would change and where it flows]

### Test coverage
- **Covered**: [files with tests that exercise the affected code]
- **Uncovered**: [affected files with no test coverage — these will break silently]

### Risk assessment
- **Safe to change**: [aspects with full test coverage and limited blast radius]
- **Needs care**: [aspects with wide blast radius or missing test coverage]
- **Suggestion**: [recommended approach — incremental migration, parallel interfaces, etc.]

Example

Analyzing lib/api.js:getAllPosts in this codebase:

### Direct dependents (4 files)
| File | Import | Usage |
|------|--------|-------|
| pages/index.js | getAllPosts | getStaticProps — fetches all posts for homepage |
| pages/posts/[slug].js | getAllPosts | getStaticProps — fetches recent posts sidebar |
| scripts/generate-rss.mjs | getAllPosts | Generates RSS feed entries |
| __tests__/lib/api.test.js | getAllPosts | 8 test cases covering sort order, field selection, filtering |

### Transitive dependents (0 files)
No re-exports found. All consumers use getAllPosts directly.

### Data flow
getAllPosts returns `[{ title, date, slug, author, ... }]`. This shape flows to:
- `pages/index.js` → `MoreStories` component → `PostPreview` component (uses title, date, slug)
- `pages/posts/[slug].js` → `RecentArticles` component (uses title, slug, date)
- `scripts/generate-rss.mjs` → RSS XML template (uses title, slug, date, description)

### Test coverage
- **Covered**: api.test.js has 8 tests — sort order, field selection, tag filtering, empty results
- **Uncovered**: scripts/generate-rss.mjs has no tests. Changes to the return shape would break RSS silently.

### Risk assessment
- **Safe**: Return value shape is well-tested. Changes to sort order or field selection would be caught.
- **Needs care**: RSS script runs at build time only, not tested in CI. Manually verify feed output after any changes.
- **Suggestion**: If changing the return shape, add a test for the RSS script first.

When to use it

  • Before refactoring a shared utility, component, or data layer function
  • When planning a breaking change to an internal API
  • Before deleting code — to confirm nothing depends on it
  • When estimating the scope of a feature that touches shared code

When NOT to use it

  • For changes to leaf nodes (a page component that nothing imports) — the blast radius is obviously just that file
  • For adding new exports — nothing depends on them yet

Tips

  • If the blast radius is wider than expected, consider an incremental approach: add the new interface alongside the old one, migrate consumers one at a time, then remove the old interface.
  • Pay special attention to the "uncovered" section. Those are the files that will break silently — and they're where bugs actually ship.
  • For large codebases, the transitive analysis can get deep. Ask Claude to stop at 3 levels of depth and summarize beyond that rather than exhaustively listing every file.