HomeSpecialized Use CasesCode Migration & Refactoring
advanced15 min read· Module 13, Lesson 4

🔄Code Migration & Refactoring

Use Claude to migrate codebases, upgrade frameworks, and refactor at scale

Code Migration & Refactoring

Large-scale code migration is one of the most tedious and error-prone tasks in software engineering. Whether you are upgrading a framework, switching languages, or modernizing a legacy codebase, Claude Code can dramatically accelerate the process while reducing the risk of regressions.

This lesson covers battle-tested strategies for using Claude Code to perform migrations that would normally take weeks or months of manual effort.


Why Migrations Are Hard

Migrations involve far more than search-and-replace. They require:

  • Semantic understanding of the old and new patterns
  • Context awareness across files that depend on each other
  • Incremental validation to catch breakages early
  • Rollback safety in case something goes wrong

Claude Code excels at all of these because it can hold your entire project context in memory, understand the intent behind code, and apply consistent transformations across hundreds of files.


Migration Strategy Overview

Every successful migration follows a predictable lifecycle:

Phase 1: Audit → Understand what needs to change Phase 2: Plan → Define the transformation rules Phase 3: Transform → Apply changes file-by-file or in batches Phase 4: Validate → Run tests, linters, and type checks Phase 5: Stabilize → Fix edge cases and regressions Phase 6: Document → Record what changed and why

Let us walk through each phase with Claude Code.


Phase 1: Auditing the Codebase

Before changing anything, you need a clear picture of the current state.

Scanning for patterns

Terminal
# Ask Claude Code to audit your codebase claude "Scan this project and list every file that uses React class components. Group them by directory and note which ones have state, lifecycle methods, or refs. Output a markdown table."

Generating a migration manifest

Terminal
claude "Create a migration-manifest.json that lists every file needing migration from Express to Fastify. For each file include: path, complexity (low/medium/high), dependencies on Express-specific APIs, and estimated effort."

This gives you a structured plan you can track with your team.


Phase 2: Defining Transformation Rules

Claude Code works best when you give it explicit rules to follow.

Creating a migration guide

Terminal
claude "Create a MIGRATION_RULES.md file that defines how to convert Express middleware to Fastify plugins. Include: 1. Route handler signature changes 2. Middleware → plugin/hook equivalents 3. Error handling differences 4. Request/response API mappings Use concrete before/after code examples for each rule."

Setting up CLAUDE.md for consistency

Add migration-specific instructions to your project's CLAUDE.md so every Claude Code session follows the same rules:

Markdown
# Migration Rules - When converting Express routes to Fastify, use the schema-based validation - Replace req.body with request.body and use Fastify's built-in parsing - Convert app.use() middleware to Fastify register() plugins - Preserve all existing test assertions, only change the setup code

Framework Upgrades

React Class Components to Hooks

This is one of the most common migration scenarios. Claude Code handles it well because the transformation is pattern-based but requires semantic understanding.

Terminal
claude "Convert src/components/UserProfile.jsx from a class component to a functional component with hooks. Requirements: - Convert this.state to useState - Convert componentDidMount to useEffect with empty deps - Convert componentDidUpdate to useEffect with appropriate deps - Convert componentWillUnmount to useEffect cleanup - Convert this.handleX methods to const handlers - Preserve all prop types - Keep the same export signature"

Handling complex class patterns

Some class components have patterns that do not map one-to-one to hooks:

Terminal
claude "This component uses getDerivedStateFromProps and componentDidCatch. Convert to hooks using: - useMemo or direct computation for derived state - An ErrorBoundary wrapper component (keep as class) for componentDidCatch Explain any patterns that cannot be directly converted to hooks."

Express to Fastify

Terminal
claude "Migrate src/routes/auth.js from Express to Fastify. Follow these rules: - Convert router.get/post to fastify.get/post - Replace express.Router() with a Fastify plugin function - Convert middleware chains to Fastify preHandler hooks - Add JSON Schema validation for request bodies - Update error handling to use Fastify's reply.code().send() - Keep the same business logic untouched"

JavaScript to TypeScript

This is a migration where Claude Code truly shines because it can infer types from runtime usage patterns.

Terminal
claude "Convert src/utils/api-client.js to TypeScript. Requirements: - Infer types from usage patterns across the codebase - Create interfaces for all function parameters and return types - Use strict mode (no 'any' types unless absolutely necessary) - Add generics where the function handles multiple types - Rename the file to .ts and update all imports across the project"

Language Migrations

Python 2 to Python 3

Terminal
claude "Migrate src/legacy/data_processor.py from Python 2 to Python 3. Handle these specific changes: - print statements to print() functions - unicode/str to str/bytes - dict.iteritems() to dict.items() - xrange to range - Update exception syntax - Fix integer division behavior - Update imports (ConfigParser, urllib, etc.) Preserve all existing functionality and add type hints."

CommonJS to ES Modules

Terminal
claude "Convert this project from CommonJS to ES Modules. For each file: - Change require() to import statements - Change module.exports to export/export default - Update dynamic requires to dynamic import() - Add .js extensions to relative imports - Update package.json to add type: module List any circular dependencies that need manual resolution."

API Version Upgrades

When an API you depend on releases a breaking version, Claude Code can handle the upgrade across your entire codebase.

Terminal
claude "Upgrade all Stripe API calls from v2022-11-15 to v2024-04-10. Read the Stripe changelog and apply these changes: - Update the stripe package to the latest version - Fix all deprecated API calls - Update webhook event type names - Adjust error handling for new error types - Update TypeScript types if using @types/stripe Show me a diff summary before applying changes."

Handling breaking changes systematically

Terminal
claude "I need to upgrade from Next.js 13 to Next.js 15. 1. First, scan the project and list all breaking changes that affect us 2. Create a prioritized migration plan 3. Start with the lowest-risk changes (config files, imports) 4. Then handle the app router migration 5. Finally update any changed APIs Do NOT change test files yet - I want to validate each step."

Database Migration Generation

Claude Code can generate database migrations based on schema changes.

Terminal
claude "Compare the current Prisma schema with the production database schema in schema.backup.prisma. Generate a migration that: - Adds the new fields without dropping existing data - Handles column renames with data preservation - Creates indexes for the new query patterns - Includes a rollback migration - Is safe for zero-downtime deployment"

ORM migration between frameworks

Terminal
claude "Migrate our data access layer from Sequelize to Prisma. For each model: - Create the equivalent Prisma schema definition - Convert query methods to Prisma Client calls - Handle associations/relations mapping - Update transaction patterns - Preserve all existing query behavior Generate a prisma/schema.prisma and update all repository files."

Batch File Processing with Claude Code

For large migrations, processing files one at a time is too slow. Use Claude Code's ability to work across multiple files.

Processing entire directories

Terminal
claude "Convert ALL .jsx files in src/components/ to TypeScript (.tsx). For each file: 1. Infer prop types from usage and create interfaces 2. Add proper return types to components 3. Type all useState, useRef, and useContext calls 4. Type event handlers with React event types 5. Rename the file from .jsx to .tsx Process them in dependency order (leaf components first)."

Using shell scripts with Claude Code

For very large codebases, combine shell scripting with Claude Code:

Terminal
# Process files in batches for file in src/legacy/*.js; do claude "Convert $file from JavaScript to TypeScript. Follow the rules in MIGRATION_RULES.md. Run tsc --noEmit after conversion to verify types." --no-interactive done

Tracking migration progress

Terminal
claude "Check our migration progress: - Count files already migrated to TypeScript (.ts/.tsx) - Count files still in JavaScript (.js/.jsx) - List the remaining files sorted by import count (most imported first) - Calculate the percentage complete Update migration-tracker.md with the current status."

Testing Migration Results

Migration without testing is just introducing bugs with extra steps.

Generating comparison tests

Terminal
claude "For each migrated component in src/components/: 1. Check if a test file exists - if not, create one 2. Verify the test covers the same behavior as before migration 3. Add snapshot tests to catch unintended rendering changes 4. Run the test suite and report any failures 5. For failures, determine if the test or the migration is wrong"

Type checking as validation

Terminal
claude "Run tsc --noEmit on the entire project and fix all type errors. For each error: - If it is a missing type, add the correct type - If it is an incompatible type, check the migration logic - If it is a third-party type issue, add a @ts-expect-error with explanation Do NOT use 'any' to suppress errors."

Integration testing after migration

Terminal
claude "Create an integration test that verifies our Express-to-Fastify migration did not break any API contracts. The test should: - Hit every endpoint with the same requests as our old Express tests - Compare response status codes, headers, and body shapes - Verify error responses match the old format - Check that middleware behavior (auth, rate limiting) is preserved"

Rollback Strategies

Every migration needs an escape hatch.

Git-based rollback

Terminal
# Create a migration branch with clear checkpoints claude "Set up our migration with git checkpoints: 1. Create a branch 'migration/express-to-fastify' 2. After each file migration, create a commit with a descriptive message 3. After each batch, create a tag like 'migration-checkpoint-1' 4. Document the rollback command for each checkpoint in ROLLBACK.md"

Feature flags for gradual migration

Terminal
claude "Set up feature flags so we can run both Express and Fastify in parallel: - Create a MIGRATION_FLAGS.ts config file - Add a flag for each migrated route group - Implement a proxy that routes to old or new handler based on the flag - Add logging to compare response times between old and new"

Dual-running for safety

Terminal
claude "Implement a shadow-mode migration validator: - For each request, run both the Express and Fastify handler - Compare the responses (status, body, headers) - Log any discrepancies without affecting the user - Send metrics to our monitoring dashboard This lets us validate the migration with real production traffic."

Practical Example: Migrating a JavaScript Project to TypeScript

Let us walk through a complete, real-world migration step by step.

Step 1: Initialize TypeScript

Terminal
claude "Initialize TypeScript in this project: - Install typescript and necessary @types packages - Create a tsconfig.json with strict mode enabled - Configure paths, baseUrl, and module resolution - Set allowJs: true so we can migrate incrementally - Add a build script to package.json"

Step 2: Start with utility files

Terminal
claude "Start the TS migration with src/utils/ (lowest dependency count): - Convert each .js file to .ts - Add explicit types to all exported functions - Create shared type definitions in src/types/ - Run tsc --noEmit to verify - Fix any errors before moving to the next file"

Step 3: Migrate the data layer

Terminal
claude "Migrate src/models/ and src/repositories/ to TypeScript: - Create interfaces for all database entities - Type all query functions with proper generics - Add return type annotations to all repository methods - Ensure nullable fields are properly typed with | null - Validate with tsc --noEmit"

Step 4: Migrate the API layer

Terminal
claude "Migrate src/routes/ and src/middleware/ to TypeScript: - Type all request handlers with Express.Request and Express.Response - Create interfaces for request bodies and query params - Type middleware functions properly - Add error types for custom error classes - Verify all routes still work with npm test"

Step 5: Migrate components (if applicable)

Terminal
claude "Migrate all React components in src/components/ to TypeScript: - Create prop interfaces for each component - Type all hooks usage (useState<Type>, useRef<Type>) - Type context values and providers - Convert .jsx to .tsx - Run the test suite after each batch"

Step 6: Remove JavaScript allowance

Terminal
claude "Finalize the TypeScript migration: - Set allowJs: false in tsconfig.json - Remove any remaining .js files (should be none) - Enable strict: true and fix all new errors - Update CI/CD pipeline to run tsc --noEmit - Update the README with new development instructions - Create a migration completion report"

Best Practices for Large-Scale Migration

  1. Migrate incrementally - Never try to migrate everything at once. Use allowJs or equivalent flags to let old and new code coexist.

  2. Test after every change - Run your test suite after each file or batch. If tests break, fix them before moving on.

  3. Keep the migration atomic per feature - Migrate a complete feature (model + routes + tests) rather than all models, then all routes.

  4. Use CLAUDE.md rules - Document your migration patterns so Claude Code applies them consistently across every session.

  5. Preserve git history - Use small, descriptive commits so you can trace what changed and roll back specific transformations.

  6. Monitor after deployment - Even after tests pass, watch error rates, performance metrics, and logs closely after deploying migrated code.

  7. Document decisions - Record why you chose certain type annotations, pattern replacements, or architectural decisions during the migration.


Summary

Code migration with Claude Code follows a disciplined process:

PhaseClaude Code Helps With
AuditScanning patterns, generating manifests
PlanCreating transformation rules, writing CLAUDE.md
TransformApplying changes across files with semantic understanding
ValidateRunning tests, fixing type errors, catching regressions
StabilizeEdge case handling, integration testing
DocumentMigration reports, updated documentation

By combining Claude Code's understanding of code semantics with a structured migration process, you can tackle migrations that would otherwise be too risky or time-consuming to attempt.