TechLead
Lesson 12 of 25
6 min read
AI-Native Engineering

Large-Scale Refactoring with AI

Learn how to use AI for large-scale refactoring — migrating JavaScript to TypeScript, extracting components, renaming across codebases, and executing the strangler fig pattern

Why AI Excels at Refactoring

Refactoring has always been high-effort, high-risk, and low-glory. You spend days making mechanical changes across hundreds of files, hoping you do not break anything, and the end result looks the same to users. This is exactly why AI transforms refactoring from a dreaded chore into a routine activity.

AI is perfect for refactoring because most refactoring is mechanical transformation — applying the same pattern change across many files. The AI does the tedious work. You review the diffs and handle the edge cases that require human judgment.

Refactoring Tasks AI Handles Best

  • Language Migration: JavaScript to TypeScript, CommonJS to ESM
  • Pattern Migration: Class components to hooks, callbacks to async/await
  • API Migration: REST to GraphQL, one ORM to another
  • Rename/Move: Renaming concepts across an entire codebase
  • Extraction: Pulling shared code into utilities, components, or packages
  • Dead Code Removal: Finding and removing unused exports, files, dependencies

Migration Walkthrough: JavaScript to TypeScript

Here is a complete walkthrough of migrating a 50-file JavaScript project to TypeScript using Claude Code. This is one of the most common and impactful refactoring tasks.

# Phase 1: Setup (5 minutes)
> /plan
> I want to migrate this entire project from JavaScript to TypeScript.
  The project has ~50 .js files in app/. Plan the migration step by step.
  Do NOT execute anything yet.

# Claude creates a plan:
# 1. Install TypeScript and configure tsconfig.json
# 2. Rename .js files to .ts/.tsx (React files)
# 3. Add type annotations to all functions
# 4. Fix all type errors
# 5. Update build configuration
# 6. Verify tests still pass

# Phase 2: Infrastructure (2 minutes)
> Execute phase 1: Install TypeScript, create a strict tsconfig.json,
  and update the build scripts in package.json. Do not touch any
  source files yet.

# Phase 3: File-by-file migration (20-30 minutes)
> Start migrating files to TypeScript. Begin with the leaf files
  (files that do not import other project files) since they are
  easiest. Rename .js to .ts/.tsx, add type annotations, and fix
  errors. After each batch of 5 files, run the type checker and
  show me the status.

# Phase 4: Complex files (10-15 minutes)
> Now migrate the remaining files that have inter-file dependencies.
  Start with the most-imported files (utilities, types, hooks) and
  work outward. Run the type checker after each file.

# Phase 5: Verify (5 minutes)
> Run the full type checker (npx tsc --noEmit) and the test suite.
  Fix any remaining errors. Show me a summary of all changes.

The Strangler Fig Pattern with AI

The strangler fig pattern — gradually replacing old code with new code while both coexist — is much more practical with AI. Instead of a big-bang rewrite, you migrate one module at a time with AI handling the mechanical work.

# Strangler fig: Migrating from REST API client to GraphQL

# Step 1: Create the new GraphQL client alongside the old REST client
> Create a new GraphQL client in app/lib/graphql-client.ts that
  mirrors the same interface as our existing REST client in
  app/lib/api-client.ts. Both should export the same function
  signatures so we can swap them incrementally.

# Step 2: Migrate one endpoint at a time
> Migrate the getUsers and getUserById functions from the REST
  client to the GraphQL client. Update only the files in
  app/features/users/ to use the new GraphQL functions. Keep
  all other features on the REST client for now.

# Step 3: Verify and continue
> Run the user feature tests. If they pass, migrate the next
  feature: products. Follow the same approach.

# Step 4: Clean up when done
> All features are now on the GraphQL client. Remove the old
  REST client and all unused REST-related code. Update imports
  across the codebase.

Common Refactoring Operations

Refactoring Type Claude Code Prompt Time Saved
Rename concept"Rename 'subscription' to 'membership' everywhere — files, variables, types, UI text"Hours → Minutes
Extract component"Extract the chart card pattern from Dashboard and Analytics into a shared ChartCard component"30 min → 3 min
Split god file"Split utils.ts (800 lines) into dateUtils, formatUtils, validationUtils, and arrayUtils. Update all imports."1 hour → 5 min
Callbacks to async/await"Convert all callback-based functions in app/services/ to async/await. Preserve error handling."2 hours → 10 min
Remove dead code"Find all exported functions in app/lib/ that are never imported anywhere. List them, then remove them."1 hour → 5 min

Using Plan Mode for Large Refactors

# Always use plan mode for refactors that touch 10+ files
> /plan
> I need to extract our authentication logic from being spread across
  12 different files into a centralized auth module. Currently, auth
  checks are duplicated in each API route, session management is
  split between 3 hooks, and token refresh logic is in the layout
  component. Plan a clean refactoring that:
  1. Creates a centralized app/lib/auth/ module
  2. Moves all auth logic there
  3. Updates all consumers to use the centralized module
  4. Maintains all existing behavior (no functional changes)
  5. Adds proper TypeScript types for all auth-related data

# Review the plan carefully before executing
# Check: Does the plan miss any files? Does the order make sense?
# Then: "Execute this plan. After each major step, run the type
# checker and tests to verify nothing is broken."

Refactoring Safety Net

Before any large refactoring, make sure you have: (1) a clean git state with all changes committed, (2) a passing test suite, and (3) a way to verify the refactoring did not change behavior. If you lack tests, ask Claude to write characterization tests first — tests that capture the current behavior, so you can verify it does not change during the refactor.

Make Refactoring Routine

With AI, refactoring should no longer be a scary, rare event. Make it routine. See a messy file? Refactor it in 5 minutes. Notice duplicated code? Extract it immediately. Technical debt no longer needs to accumulate because the cost of paying it down has dropped by 90%. The AI-native engineer keeps the codebase clean as a daily habit, not a quarterly initiative.

Summary

AI turns large-scale refactoring from a risky, multi-day project into a routine, multi-hour task. Use plan mode for safety, migrate incrementally with the strangler fig pattern, and always verify with tests. The biggest shift: refactoring is now cheap enough to do constantly, which means your codebase stays clean instead of accumulating tech debt. Make refactoring a daily habit, not a quarterly crisis.

Continue Learning