Skip to main content
Codemods are powerful automation tools, but they’re not always the best solution. This guide helps you decide when to use codemods versus manual migration.

When Codemods Excel

Codemods are ideal for transformations that are:

Mechanical and Repetitive

If the change follows a consistent pattern across many files, codemods save hours of manual work. Perfect for codemods:
// Replacing deprecated util.is* methods across 100+ files
util.isArray(value)     → Array.isArray(value)
util.isString(value)    → typeof value === 'string'
util.isFunction(value)  → typeof value === 'function'
Not ideal for codemods:
// Complex refactoring requiring business logic understanding
function processUser(user) {
  // Needs context-aware restructuring of data flow
  // Different in each file based on use case
}

Syntactically Deterministic

The transformation can be determined purely from the code structure without external context.
Codemods work best when the “before” and “after” states are clear and unambiguous from the code alone.
Perfect for codemods:
// API renaming - always transforms the same way
fs.rmdir(path, { recursive: true })  →  fs.rm(path, { recursive: true })
Not ideal for codemods:
// Requires understanding of runtime behavior
const config = loadConfig(); // What's in config?
if (config.useNewAPI) {      // Runtime conditional
  // Should this be transformed?
}

High-Volume Changes

The more locations need changing, the more valuable automation becomes.
1

1-5 occurrences

Manual migration is usually faster than writing a codemod
2

5-20 occurrences

Consider codemods if the pattern is very consistent
3

20+ occurrences

Codemods are almost always worth it
4

Multiple repositories

Codemods are essential - impossible to do manually at scale

Common Use Cases

1. Deprecation Migrations

Replacing deprecated APIs with their modern equivalents. Example: util.is* to native checks
// Before
const util = require('util');
if (util.isArray(data)) {
  // ...
}

// After
if (Array.isArray(data)) {
  // ...
}
Why codemods work well:
  • Pattern is consistent across all usages
  • No semantic changes to behavior
  • High volume in typical codebases
  • Simple 1:1 replacement mapping

2. Import/Require Modernization

Example: CommonJS to ESM
// Before
const fs = require('fs');
const { readFile } = require('fs/promises');

// After
import fs from 'fs';
import { readFile } from 'fs/promises';
Why codemods work well:
  • Syntactic transformation with clear rules
  • Can handle various destructuring patterns
  • Large-scale impact across entire codebase

3. API Signature Changes

Example: Updated function signatures
// Before
fs.exists(path, (exists) => {
  if (exists) handleFile();
});

// After
fs.access(path, (err) => {
  if (!err) handleFile();
});
This type of transformation requires careful testing because the semantics change (error-first callback vs boolean callback).

4. Configuration Updates

Example: package.json script modifications
// Before
{
  "scripts": {
    "test": "node --experimental-modules test.js"
  }
}

// After
{
  "scripts": {
    "test": "node test.js"
  }
}
Why codemods work well:
  • JSON is highly structured
  • Can use AST-based package.json utilities
  • Consistent patterns across projects
See the package.json utilities for examples.

5. Node.js Version Upgrades

Handling breaking changes when upgrading Node.js versions. Example: Buffer constructor deprecation
// Before (deprecated in Node.js 10)
new Buffer(10);
new Buffer([1, 2, 3]);
new Buffer('text');

// After
Buffer.alloc(10);
Buffer.from([1, 2, 3]);
Buffer.from('text');
Why codemods work well:
  • Official Node.js guidance is clear
  • Transformation rules are well-defined
  • Affects many projects upgrading Node.js
  • High risk of missing instances manually

When to Avoid Codemods

Complex Business Logic

If the transformation requires understanding of:
  • Domain-specific requirements
  • Business rules and edge cases
  • User intent beyond code structure
Example:
// Refactoring authentication flow - too context-dependent
function authenticate(user) {
  // Each project has different requirements
  // Needs human judgment on security implications
}

Rare or Unique Patterns

If your codebase has unique patterns not shared with other projects:
  • One-off refactorings specific to your architecture
  • Custom framework migrations
  • Project-specific design pattern changes
For one-off refactorings, use search-and-replace with regular expressions or your IDE’s refactoring tools.

Semantic Changes Requiring Analysis

When the transformation changes behavior in ways that need validation:
// Changing error handling strategy
try {
  await riskyOperation();
} catch (err) {
  // Different error handling needed based on context
  // Humans must decide the right approach
}

Small Codebases

If you only have a few files to change, manual migration is often faster:
  • Writing a codemod: 2-4 hours
  • Testing thoroughly: 1-2 hours
  • Manual changes + review: 30 minutes for small projects

Performance-Critical Code

When optimizations matter more than automation:
// Hot path requiring careful performance tuning
for (let i = 0; i < million; i++) {
  // Each optimization needs manual review
  // Automated transformation might miss perf implications
}

Hybrid Approach: Codemods + Manual Review

Many successful migrations use a combined strategy:
1

Automate the mechanical parts

Use codemods for straightforward 1:1 replacements
2

Flag edge cases

Have codemods add TODO comments or skip uncertain transformations
3

Manual review and cleanup

Developers review changes and handle flagged cases
4

Test thoroughly

Run full test suite to catch any issues
Example: Conservative codemod
// Codemod can transform simple cases
util.isArray(x) → Array.isArray(x)

// But adds TODO for complex ones
const check = someCondition ? util.isArray : util.isObject;
// → /* TODO: Review this util.is* usage - codemod skipped */

Decision Framework

Use this checklist to decide:
QuestionYes → CodemodNo → Manual
Are there 20+ locations to change?
Is the pattern consistent across files?
Can you define clear transformation rules?
Is this a common Node.js upgrade scenario?
Does it require business logic understanding?
Is the change purely syntactic?
Will this help other projects too?Maybe
If you answered “Yes” to 4+ questions in the codemod column, automation is likely worth it.

Real-World Examples

✅ Great Codemod Candidate: tmpDir to tmpdir

Node.js deprecated os.tmpDir() in favor of os.tmpdir(). Why it’s perfect:
  • Simple rename (tmpDir → tmpdir)
  • No semantic changes
  • Easy to verify correctness
  • Affects many projects
  • Zero ambiguity
Codemod benefits:
  • Transforms 100% of cases correctly
  • Saves hours across the ecosystem
  • Zero risk of human error

✅ Good Codemod Candidate: http.OutgoingMessage._headers

Private property _headers became getHeaders() method. Why it works:
  • Consistent pattern: obj._headersobj.getHeaders()
  • Property access becomes method call
  • Mostly deterministic transformation
Challenges:
  • Need to handle cases like const h = obj._headers;
  • Codemod flags complex cases for manual review

❌ Poor Codemod Candidate: Express 4 to 5

Migrating Express.js across major versions. Why it’s difficult:
  • Many breaking changes with different patterns
  • Middleware signatures changed
  • Router behavior changed
  • Requires understanding of request/response flow
  • Project-specific middleware needs custom handling
Better approach:
  • Use official migration guide
  • Manual migration with careful testing
  • Focus automation on simple renames only

Getting Started

Ready to use codemods? Start here:

How Codemods Work

Learn the technical foundation of AST transformations

Safety Best Practices

Run codemods safely with version control and testing

Contributing Codemods

If you identify a good codemod candidate for the Node.js ecosystem:
  1. Check the Codemod Registry to see if it exists
  2. Review the Contributing Guide
  3. Start with the codemod structure and testing framework
  4. Share with the community!
The best codemods solve problems that many developers face. If you need it, others probably do too.

Build docs developers (and LLMs) love