AI Engineering· 6 min read

Large Refactors with AI, with Humans Still in Charge

Refactoring with AI on large codebases: make it safe first, codemods vs AI, small verifiable batches, reviewing AI diffs, and when to keep AI out.

By

Full-Stack, Mobile & AI Engineer

Large refactors stall for familiar reasons. The change touches 400 files, the work is tedious, the risk is real, and every week it's in progress the main branch moves underneath it. So teams postpone the migration until a deprecated library forces their hand, then do it in a rush.

AI coding agents change one part of that equation: the tedium. An agent can apply the same transformation across hundreds of files in an afternoon. What they don't remove is the risk, and they can quietly add some. This guide covers refactoring with AI on a real codebase: making the refactor safe before it starts, choosing between codemods and AI, working in small verifiable batches, reviewing AI-generated diffs, and knowing when to keep AI out of it.

Key takeaways

  • AI removes tedium, not risk. The safety net (tests, types, CI, staged rollout) matters more with AI, not less.
  • Use codemods for uniform changes and AI for the long tail of cases that don't fit a pattern.
  • Work in small batches that each pass every check and can be reviewed in one sitting.
  • Never let the agent change tests to make them pass within the same change as the refactor.
  • Humans decide the target design, review every diff and own the release.

What AI changes about refactoring, and what it doesn't

Without AIWith AI
Typing the changesSlow, error-prone, boringFast
Understanding unfamiliar codeHours of readingFaster with good questions, but verify answers
Risk of behaviour changesRealReal, plus plausible-looking mistakes
Review loadProportional to the changeHigher, because changes arrive faster
Knowing what the target should beA human decisionStill a human decision

The bottleneck moves from writing to reviewing. Plan for that, or the time you save writing code gets spent untangling a giant diff nobody can read.

Before you start: make the refactor safe

  • Pin down behaviour with tests. If the code has no tests, write characterisation tests first: tests that record what the code does today, so you know if the refactor changes it. AI is good at drafting these, and you're good at checking they test the right thing.
  • Turn on strict type checking if you can. Types catch a large class of refactoring mistakes for free.
  • Make CI the gate: type check, lint, tests and a build on every batch.
  • Record baselines: bundle size, startup time, crash-free sessions and key screen performance, so you can compare after.
  • Write the target down. A short migration guide in the repository ("we're moving from X to Y; here's the before and after pattern; here are the exceptions") becomes the agent's instructions and the reviewer's checklist.

Codemods or AI?

Use a codemod when...Use AI when...
The change is uniform and mechanicalCases vary and need judgement
An official codemod existsThe pattern has many exceptions
You need the same result every runCode needs restructuring, not just renaming
The change touches thousands of call sitesTests or types need writing alongside

Official codemods are the best starting point when they exist. Expo ships one for moving Expo Router imports off React Navigation in SDK 56 (npx expo-codemod sdk-56-expo-router-react-navigation-replace), and React Native Testing Library ships codemods for its v14 async changes. Tools such as jscodeshift and ast-grep let you write your own. A sensible pattern is codemod first, then AI for whatever the codemod couldn't handle, then human review of both.

A workflow that keeps humans in charge

  1. Decide the target design yourself. Write the migration guide with before and after examples.
  2. Pilot on one module. Let the agent migrate a single directory, review it deeply, and refine the instructions based on what it got wrong.
  3. Batch by area (one feature folder, one package) with one pull request per batch, small enough to review properly.
  4. Run every check on every batch: type check, lint, tests and a build. The agent should run them too and fix failures before you look.
  5. Review every diff with the checklist below. Reject batches that mix the refactor with unrelated "improvements".
  6. Release gradually with a phased rollout or feature flags, and compare against your baselines.
  7. Delete the old path once everything is migrated, so the codebase doesn't carry both forever.

A small example: a library API change across an app

When a library changes its API, the same edit is needed in many places. MMKV v4, for instance, creates storage instances with a function instead of a constructor:

// Before (v3)
import { MMKV } from "react-native-mmkv";
export const storage = new MMKV();

// After (v4)
import { createMMKV } from "react-native-mmkv";
export const storage = createMMKV();

A codemod or a search handles the obvious cases. AI helps with the rest: instances created inside helpers, wrappers that subclass or mock the old class, and tests that construct storage differently. The upgrade also adds react-native-nitro-modules as a dependency and needs a native rebuild, which is exactly the kind of step a person should own, along with reading the full upgrade guide for any other API changes.

From the field: I use AI tools (Claude Code and Cursor) for large-scale refactors and framework migrations, and every change is reviewed by me before merge. I've also completed 8 migrations to React Native's New Architecture, and cut load time by about 60% (3.8 seconds to 1.5) through Hermes, image optimisation and cache-first fetching. Migrations go well when the target is clear, the batches are small and the release is staged, whoever types the code.

Reviewing AI-generated refactor diffs

Read AI refactors with specific suspicions:

  • Changed tests. If a test changed in the same diff as the code it tests, ask why. An agent asked to "make the tests pass" may change the test instead of the code. Keep test changes in separate, deliberate commits.
  • Behaviour hidden in cleanups. Reordered conditions, changed defaults, removed null checks, swallowed errors.
  • Deleted code. Error handling, logging and edge cases that looked unnecessary.
  • Broken contracts: renamed exports, changed function signatures and altered API payloads that other code, or older app versions, depend on.
  • New dependencies nobody asked for.
  • Formatting noise that hides the real change. Run the formatter in a separate commit.

When to keep AI out of a refactor

  • No clear target. If the team hasn't agreed what "after" looks like, an agent will invent one.
  • Security-critical code such as authentication, payments and permissions, where every line needs deliberate human attention.
  • No safety net and no time to build one. Without tests or types, fast changes are just fast risk.
  • Tiny changes. Explaining the task can take longer than doing it.

FAQ

Can AI refactor a whole codebase?

It can make the edits across a whole codebase, but it shouldn't do it in one pass. Humans decide the target design, the work goes in small batches that each pass type checks, tests and a build, and a person reviews every diff before release.

How do I stop an AI agent from breaking things during a refactor?

Build a safety net first: characterisation tests, strict types and CI on every batch. Give the agent a written migration guide, keep batches small, forbid test changes in the same commit as code changes, review every diff, and release gradually against baseline metrics.

Should I use codemods or AI for a migration?

Use official or custom codemods for uniform, mechanical changes, because they're deterministic and repeatable. Use AI for cases that vary, need restructuring or need tests written alongside. Combining both, codemod first and AI for the remainder, usually works best.

How do I review a large AI-generated diff?

Don't accept one. Split the work into batches small enough to review in one sitting, separate formatting and test changes into their own commits, and review with a checklist: changed tests, hidden behaviour changes, deleted error handling, broken contracts and new dependencies.

Next steps

If a migration has been sitting on your backlog because it's too big and too risky, I can plan it, run it in safe batches with AI assistance, and keep your releases stable throughout. Get in touch, or see my services.

Sources

Bhuwanesh Sisodia

Bhuwanesh Sisodia

Full-Stack, Mobile & AI Engineer

Bhuwanesh Sisodia is a Full-Stack, Mobile & AI Engineer based in Mumbai, with 7+ years of experience and 50+ apps shipped to the App Store and Google Play. He works independently with startups and product teams worldwide.

About meGitHubLinkedInX

Related