17

Inherited a single def run() that was 600 lines: arg parsing, file IO, business logic, and printing all tangled together. Management wanted features added but touching it was Russian roulette. Asking a model to "clean it up" produced a beautiful rewrite that subtly changed three edge cases and broke a downstream script parsing its output.

The prompt that actually worked forbids behavior changes, requires a characterization-test-first approach, and makes the model prove equivalence by keeping the exact stdout/exit-code contract. Pinning the observable contract before refactoring is the whole game.

Curious how others capture the 'golden' output for a CLI that has side effects on the filesystem. I've been running it in a temp dir and snapshotting the tree.

THE PROMPT
Refactor this Python CLI function without changing its observable behavior. This is a behavior-preserving refactor, NOT a redesign.

Hard constraints:
- Do NOT change stdout/stderr output byte-for-byte, exit codes, flags, or file side effects. If you think a current behavior is a bug, leave it and note it in a BUGS-I-FOUND list; do not fix it in this pass.
- Step 1, before refactoring: write characterization tests that capture the CURRENT behavior for {LIST_KEY_INPUTS} (normal, empty, malformed, and boundary inputs), including exit code and exact stdout. These tests must pass against the original code.
- Step 2: extract cohesive pure functions (parse, compute, format, io) from the god-function. Keep the public entry point and its signature/CLI contract identical.
- Step 3: run the characterization tests against the refactored version and confirm they still pass unchanged.
- No new dependencies. No change to the CLI's argument grammar. Preserve error message text exactly (downstream may grep it).
- Prefer small, obviously-correct moves over clever restructuring.

Output: the characterization tests, then the refactored code, then a short EQUIVALENCE note explaining why behavior is preserved and listing anything you deliberately left alone.
1"If you think it's a bug, leave it and note it" is the line that keeps these refactors from turning into a scope explosion. Ship the equivalence first, fix bugs in a separate PR.shipfast 1 month ago
add a comment

1 Answer

14

Characterization-tests-first is the correct instinct and most people skip straight to the rewrite. For the filesystem side effects, snapshotting the temp-dir tree works but gets flaky with timestamps/ordering. I normalize: sort the file list, hash contents, and strip volatile fields (mtimes, absolute temp paths) before comparing. Then the golden snapshot is stable across runs and machines.

THE PROMPT
For side-effect capture: run in a fresh tmp dir, then produce a normalized manifest = sorted list of (relative_path, sha256(content)) with mtimes and absolute paths stripped. Compare manifests before/after refactor.

Your Answer