The Problem: Large-Scale Code Changes
You're working on a project, and a dependency you rely on releases a breaking change. Suddenly, you're staring at hundreds of files that need to be updated: renamed functions, changed APIs, restructured imports.
You could do it by hand. But that's slow, error-prone, and frankly miserable when you're dealing with a large codebase.
You might think: "I'll just use find and replace." But find and replace doesn't understand your code. It works on raw text, not on the meaning of your code. Consider this simple example:
If you try to find-and-replace hello with world, you'd break the function name sayHello, modify the string "hello world", and cause chaos. Find and replace doesn't know the difference between a variable name, a function name, and a string literal.
This is the fundamental problem. What you need is a tool that understands the structure of your code, not just its text.
What Is a Codemod?
A codemod (short for "code modification") is an automated code transformation. It's a program that reads your source code, understands its structure, and rewrites it according to rules you define.
Unlike find and replace, codemods work with the Abstract Syntax Tree (AST), a structured representation of your code that separates variable names from function names from string literals. This means codemods can make precise, targeted changes without breaking unrelated code.
Codemods are commonly used for:
- Framework migrations like upgrading from React class components to hooks, or from Next.js pages router to app router
- API deprecations where old API calls need to be replaced with their new equivalents across an entire codebase
- Large-scale refactors such as renaming functions, restructuring imports, or updating patterns consistently across hundreds of files
- Language modernization like converting
vartoconst/let, adopting optional chaining, or migrating to ES modules
The key insight is that these tasks are mechanical. They follow clear, repeatable rules. A human could do them, but a codemod does them faster, more consistently, and without missing edge cases.
How Codemods Work: The AST
At the core of every codemod is the Abstract Syntax Tree (AST). Here's how the process works, in three steps:
Step 1: Parse. The codemod reads your source code and parses it into an AST. This tree represents the structure of your code: which parts are variable declarations, which are function calls, which are string literals, and so on.
Step 2: Transform. The codemod walks the tree and applies transformations. For example, it might find all nodes that represent var declarations and change them to const or let, depending on whether the variable is reassigned.
Step 3: Generate. The modified tree is converted back into source code, preserving your original formatting as much as possible.
Let's make this concrete. Take this simple piece of code:
The AST for this code represents each part of the program as a node in a tree. The var name = "world" line becomes a VariableDeclaration node containing a VariableDeclarator with the name name and the value "world". The console.log(name) line becomes a CallExpression node. Every element of your code has a precise, labeled place in the tree.
A codemod that converts var to const would find all VariableDeclaration nodes where the kind is var, check if the variable is ever reassigned (if yes, use let; if no, use const), and replace the var keyword accordingly.
The result:
This is a trivial example, but the same principle scales to complex transformations across thousands of files.
A Real Codemod Example
Here's what a real codemod looks like. This transform converts var declarations to const or let using JSSG, a secure JavaScript runtime for codemods that has ast-grep built in as a module:
This codemod uses the pattern "var $DECL" to find all var declarations in the AST, checks whether each variable is reassigned (mutated), and replaces the var keyword with const or let accordingly. The full implementation handles additional edge cases like hoisted variables, loop closures, and duplicate declarations. You can see the complete version in Writing Codemods Like a Pro.
Approaches to Writing Codemods
There are several approaches to writing codemods, each with different trade-offs:
Pattern Matching (Declarative)
The most accessible approach. You write patterns that describe the code you want to find and specify how to replace it. ast-grep lets you express transforms as concise YAML rules:
This finds every console.log() call and replaces it with logger.info(), preserving the argument. Pattern matching is best for straightforward, well-defined transforms where the before and after are clear.
Programmatic (Imperative)
For complex transforms, you write JavaScript or TypeScript code that walks the AST and applies conditional logic. This gives you full control: you can inspect parent nodes, check sibling relationships, and handle arbitrary edge cases. The var to const/let example above follows this approach, using ast-grep's pattern matching inside JavaScript code that decides how to handle each match.
JSSG is purpose-built for this. It's a secure JavaScript runtime with ast-grep as a built-in module, so you get the expressiveness of JavaScript with the speed and precision of ast-grep pattern matching in a single tool.
Hybrid (AST + AI)
Some migrations are too nuanced for pure pattern matching but too large to handle manually. This is where combining AST-based and AI-powered steps becomes valuable.
In a Codemod workflow, you can chain multiple step types together. A common pattern is to run deterministic AST transforms first (fast and precise), then let an AI step handle the remaining edge cases that are hard to express as patterns:
- A JSSG step handles the well-defined, mechanical transforms
- An AI step picks up the ambiguous cases the rules miss
- A shell step runs formatting or linting to clean up the result
This layered approach gives you the speed and reliability of AST transforms for the bulk of the work, with AI filling in the gaps where pattern matching falls short.
Codemods at Scale
Writing a single codemod for a single project is straightforward. But real-world codebases need dozens of codemods: each dependency upgrade, each API change, each language modernization effort. And if you're maintaining multiple repositories, the challenge multiplies.
At scale, you need more than just a way to write codemods. You need to:
- Discover existing codemods instead of writing everything from scratch
- Compose multi-step transforms, because a migration often involves several changes that need to happen together
- Run codemods safely across repositories with dry-run, diff review, and rollback
- Track large-scale migration progress across teams and repositories
- Share codemods with your team and the community
This is where a platform like Codemod comes in:
- Registry: lets you browse and discover community-built codemods for popular migrations (React, Next.js, Nuxt, and more), so you don't have to reinvent the wheel.
- CLI: lets you run codemods locally with
npx codemod run. Preview changes before applying, see diffs, and roll back if needed. - Workflows: let you compose multi-step transforms that combine AST-based codemods, AI steps, and shell commands into a single pipeline.
- Insights: let you track migration progress across repositories and teams, so you always know what's been migrated and what's left.
- Studio: is a visual AST explorer and codemod builder. Inspect your code's tree structure, test transforms interactively, and export production-ready codemods.
Get Started
Ready to build codemods?
- Run your first codemod. Browse the Registry and find a codemod for a migration you need. Run it with
npx codemod run. - Write your own. Follow the step-by-step guide in Writing Codemods Like a Pro to build a real-world codemod from scratch.
- Explore the docs. Check out the JSSG quickstart to learn the codemod engine, or the Workflows guide to compose multi-step transforms.