Use case · Refactoring

The fearless refactor

You need to change persist() — the function at the bottom of everything. The question that keeps you up: what actually breaks if you do? And how do you stop a reviewer from rubber-stamping a change that quietly introduces a circular dependency?

impact json diff --fail-on-new-cycles

Grep tells you where a name appears. It doesn't tell you the transitive blast radius, and it can't tell you whether your change closed a dependency loop three call-hops away. The call graph can — deterministically, so the same answer goes in the PR every time.

The fixture (examples/scenarios/refactor) ships a small service in two states, before/ and after/, where the "after" refactor adds an audit hook that accidentally creates a cycle. Real commands, real output.

1

Measure the blast radius before you touch it

impact reports the transitive callers and callees of a function and how many modules they span — the "if I break this, who feels it" number.

$ py-code-visualizer impact persist examples/scenarios/refactor/before
Impact analysis for: core.persist ⚠️ Touching `persist` affects **4** transitive caller(s) across **3** module(s). Direct callers (2): ← service.cancel_order ← service.place_order Direct callees (2): → core._write → core.validate Transitive blast radius: 4 caller(s) Modules affected: core, handlers, service

Now the PR description writes itself: "Changes persist — 4 transitive callers across 3 modules. Reviewers from those areas, please look." That one line is what makes a reviewer slow down at the right moment.

2

Catch the cycle the refactor introduced

Snapshot the graph before and after as canonical JSON, then diff them. The refactor added an audit() hook that calls back into persist() — a brand-new circular dependency. The gate refuses to let it through.

$ py-code-visualizer json examples/scenarios/refactor/before -o base.json $ py-code-visualizer json examples/scenarios/refactor/after -o head.json $ py-code-visualizer diff base.json head.json --fail-on-new-cycles
## 🗺️ Architecture Change Report **Architecture Health:** C → D+ 📉 (74 → 69) ### 🔴 New circular dependencies - `persist → audit → persist` | Metric | Base | Head | Δ | |---|---|---|---| | Functions | 9 | 9 | 0 | | Call edges | 7 | 9 | +2 | | Cross-module edges | 4 | 6 | +2 | ### 🔗 Added calls (2) - `persist` → `audit` - `audit` → `persist`
exit code
$ echo $? 2 # new cycle detected — PR check fails

The reviewer sees the health grade slip C → D+, the exact new cycle, and the two edges that caused it — rendered as a Markdown comment right in the PR. Nobody has to notice the loop by hand; the gate did.

What you got. A pre-flight blast-radius number (4 transitive callers across 3 modules) that tells reviewers where to look, and a deterministic PR gate that caught a newly introduced cyclepersist → audit → persist — and failed the build (exit 2) before it merged. Refactoring stops being a leap of faith.

Every block above is verbatim output from the commands run against examples/scenarios/refactor. Wire diff into a GitHub Action to post the report as a PR comment automatically — see docs/integrations.md.

← All use cases