Big Refactor? Write a Codemod, Not Hand Edits
Once a refactor touches hundreds of lines, stop hand-editing. Manual edits at that scale are error-prone and exhausting to review: you will miss cases, and your reviewers can't trust a 500-line diff of "the same change, probably, everywhere." Write the transform instead — a codemod, a sed script, or an AST rewrite — and the change becomes mechanical, uniform, and reviewable.
Why hand edits break down at scale
A refactor like "rename this API," "swap this import," or "change this call signature" is one decision applied N times. When N is 5, your editor is fine. When N is 500:
- You miss cases. Grep finds the obvious ones; the string-concatenated, aliased, and re-exported ones slip through.
- You introduce variance. Edit 500 sites by hand and a few will be subtly different — a dropped argument here, a wrong indent there.
- Review becomes theater. No reviewer genuinely reads 500 near-identical hunks. They skim, approve, and the one wrong hunk ships.
A scripted transform inverts all three: coverage is whatever your pattern matches, every change is identical by construction, and the reviewer reads the ~20-line transform, not the 500-line output.
Pick the lightest tool that's still correct
| Change looks like | Reach for | Why |
|---|---|---|
| Exact string, no syntax risk | sed / editor multi-file replace | Fastest; fine when a false match is impossible |
| Pattern with balanced brackets, many languages | Comby | Structural match without writing parser code |
| Syntax-aware find/replace, one-liner | ast-grep | Pattern + rewrite on the AST, from the CLI |
| JS/TS transform with logic | jscodeshift | Full programmatic AST access, preserves style via recast |
| Python transform preserving comments/format | LibCST codemods | Lossless concrete syntax tree |
| Java/JVM framework migration | OpenRewrite | Prepackaged recipes over lossless semantic trees |
Rule of thumb: escalate only when the simpler tool can produce a false match. Text → structural pattern → full AST program.
What it looks like
Syntax-aware one-liner with ast-grep — rewrite var declarations to let across a TypeScript repo:
ast-grep --pattern 'var code = $PATTERN' \
--rewrite 'let code = new $PATTERN' \
--lang ts
When you need logic, a jscodeshift transform is a small JS function; run it with a dry pass first:
jscodeshift -t rename-api.js src/ --dry --print # preview
jscodeshift -t rename-api.js src/ # apply
Either way, the artifact you commit alongside the diff is the transform. That's what gets reviewed.
The payoff for review
Ship the refactor as two things: the script, and one commit that is purely its output. The reviewer checks the rule once, spot-checks a few applications, and trusts the rest — because a machine applied one rule uniformly. That's a fundamentally stronger guarantee than 500 careful keystrokes, and it's re-runnable when the branch goes stale: rebase, re-run the codemod, done.
Power-user moves
- Separate mechanical from manual. If 480 sites are mechanical and 20 need judgment, run the codemod for the 480 in one commit, then hand-edit the 20 in a second commit. Reviewers get one trivially-verifiable diff and one small real one.
- Dry-run into review.
jscodeshift --dry --printand ast-grep's interactive mode let you eyeball matches before touching disk — treat the match list as the test of your pattern. - Let the AI write the codemod, not the edits. Asking a coding agent to hand-edit 500 lines multiplies its chances to hallucinate a variant. Asking it to write a 20-line transform gives you one small artifact you can actually verify — then you run it.
- Keep the script in the repo. Under
scripts/codemods/, transforms become documentation of how the codebase evolved, and templates for the next migration.
Resources
- jscodeshift — codemod toolkit for JavaScript/TypeScript
- ast-grep — structural search and rewrite from the CLI
- LibCST codemods tutorial — Python transforms that preserve formatting
- OpenRewrite — recipe-based large-scale refactoring
- Comby — lightweight structural search-and-replace across 50+ languages
Building an AI feature? Yeda AI designs, audits, and ships production LLM systems.