vinhnx/codemods: The Rust Migration Repo That Thinks in Layers
A codemod registry that routes simple renames, context-heavy rewrites, and edge-case migrations into different tools, so breaking changes become repeatable work instead of one-off pain.

I built these codemods to automate some of the tedious refactoring tasks I encounter regularly. They are biased towards my own workflow, but I hope others find them useful too.
- vinhnx/codemods treats migration work as a routing problem, not a monolithic rewrite.
- Its packages escalate from declarative YAML to TypeScript transforms to AI skills as uncertainty rises.
- The Rust examples show why structural guards and AST-aware edits matter more than raw search and replace.
- The repo's value is curated migration judgment, not a new codemod engine.
Why breaking changes become migration debt
Most dependency upgrades are not dramatic. They are a pile of small edits that have to be right everywhere, or they become expensive cleanup later. That is the problem vinhnx/codemods is built to attack.
A breaking change is not just syntax churn. It is a trust problem. You need a migration that is deterministic enough to run across a codebase and selective enough to avoid touching the wrong spots.
The repo is a routing system, not a bucket of scripts
Each codemod lives in its own self-contained package, with its own workflow, rules, tests, scripts, and sometimes an AI skill. That structure is the point. The repo is saying that different kinds of migration work deserve different kinds of machinery.
codemods/
axum-0-7-to-0-8/
workflow.yaml
rules/
scripts/
tests/
agents/skill/
clap-v3-to-v4/
workflow.yaml
rules/
scripts/
tests/
agents/skill/
Simple changes stay declarative. Harder ones move into TypeScript, where the script can walk parents, children, ancestors, definitions, and references instead of guessing from raw text. The result is not just a search-and-replace pass. It is a structural rewrite with guard rails.
That bias is useful, not limiting. A migration registry gets better when it encodes a point of view, because each rule is a bet about where humans keep making the same mistake.
Simple renames, context-aware rewrites, long-tail migrations
The tiering is the whole design. YAML rules handle the obvious rename. TypeScript handles AST-aware logic when a change depends on context. The AI skill folders are the escape hatch for the long tail, where static rules are not enough.
The repo also uses guard functions so a method name only gets rewritten when the surrounding code says it belongs to the right crate. That prevents accidental matches in unrelated code that happens to look similar.
What the Rust examples reveal
The examples make the difference between cosmetic and semantic change easy to see. `clap` can collapse chained argument bounds into a cleaner range expression. `axum` can rewrite route parameters without mangling raw strings. `hyper`, `rand`, `tree-sitter`, and `ratatui` show the same pattern: this repo cares about meaning, not just spelling.
// before
.arg(
Arg::new("items")
.min_values(1)
.max_values(5)
)
// after
.arg(
Arg::new("items")
.num_args(1..=5)
)
That is why the `tests/` folders matter so much. Every codemod ships input and expected output pairs, which turns the repo into a regression suite as much as a migration library.
Why this is more than find-and-replace
The cleanest comparison is layered. Engines like ast-grep and jscodeshift provide the machinery. vinhnx/codemods provides the migration content and the judgment about which layer should handle each change.
| Approach | Role | What you get | What you still need |
|---|---|---|---|
| Hand edit | Manual migration | Maximum judgment | Time, consistency, repeatability |
| ast-grep or jscodeshift | Codemod engine | Structural matching and edits | Authored transforms |
| vinhnx/codemods | Curated migration registry | Prebuilt Rust-specific judgment | An engine to execute the transforms |
That separation is the real leverage. A team does not have to rediscover its migration strategy every time a dependency breaks. It can reuse a strategy that has already been encoded, tested, and narrowed down to the cases that matter.





