2-MINUTE ASSESSMENT•100% UNBIASED • NO SALES CALL

GET YOUR CUSTOM AI IMPLEMENTATION PLAYBOOK

Answer 6 quick questions to get a personalized, 14-day setup guide and prompt library built specifically for your tech stack.

How to Safely Refactor a Large Codebase With Claude Code

By Ashley Gross

·

·

Claude

Agentic coding tools feel dangerous when one bad command can touch hundreds of files. Allowing an AI agent to roam freely through a monorepo or legacy backend usually ends with broken dependencies, uncommitted draft edits, and failing test suites. You can run sweeping architectural refactors safely by setting strict execution guardrails, working in small committed steps, and enforcing git-backed review loops.

What You Need

  • Plan level: Pro, Team, or Enterprise plan (Claude Code CLI requires an active paid tier or API key access).

  • System requirements: macOS, Linux, or Windows with Node.js installed.

  • Environment: A clean git repository with passing automated test suites.

  • Setup time: 15 minutes for installation and configuration.

Step-by-Step Walkthrough

1. Set Up Safety Guardrails and Permissions

Install Claude Code in your terminal via standard native installation or Homebrew (brew install --cask claude-code). Before granting full file access, check your active permission mode using Shift+Tab in the CLI or set --permission-mode manual. Always create a dedicated git working branch before launching your session.

Why it matters: Setting explicit permission modes prevents Claude Code from running destructive terminal commands or pushing unreviewed edits directly to your primary branch.


# Create a fresh git working branch
git checkout -b refactor/express-async-await

# Launch Claude Code with manual approval mode enforced
claude --permission-mode manual

2. Write a CLAUDE.md Memory File

Create a CLAUDE.md file at the root of your repository. Store high-level facts, build commands, test instructions, and architectural guidelines inside this file. Claude Code reads this file automatically at the start of every session to align with your project conventions.

Why it matters: A central project memory file prevents you from repeating style guidelines, test commands, and architectural constraints in every prompt.


# Project Memory: Express API Service

## Build & Test Commands
- Run tests: `npm test`
- Run single test file: `npx jest path/to/file.test.js`
- Run linter: `npm run lint`

## Architecture Rules
- Use async/await syntax exclusively; do not use callback signatures.
- Wrap all route handler logic in custom try/catch middleware wrappers.
- Do not modify database schemas or migration files

3. Plan Before Editing

Ask Claude Code to analyze your target files and generate a step-by-step refactoring plan before modifying code. Review the proposed execution plan, check affected dependencies, and instruct the agent to pause for confirmation before touching files.

Why it matters: Requesting a written plan first isolates logic errors and prevents uncoordinated multi-file edits across your repository.


[PLANNING PROMPT]
Analyze our /src/controllers directory. 
Map all route handlers currently using callback signatures. 
Write a step-by-step refactoring plan to convert them to async/await across 5 distinct batches. 
Do not edit any files yet. Present the plan for review first

4. Refactor in Small, Tested, Committed Steps

Instruct Claude Code to execute the refactoring plan one module or batch at a time. After making file edits, require the agent to execute your test runner, review failures, fix broken syntax, and create a descriptive git commit before moving to the next batch.

Why it matters: Incremental testing and committing ensures each stage remains stable, making it easy to revert bad changes without losing hours of progress.


[EXECUTION & COMMIT PROMPT]
Execute Batch 1 of the refactor plan covering /src/controllers/auth.js. 
Convert callbacks to async/await syntax according to CLAUDE.md guidelines. 
Run 'npm test' to verify. 
Once all tests pass, stage the modified files and commit with a clear git message

5. Manage Context and Delegate Workload

As sessions grow longer, token usage rises and model context degrades. Run the /compact command to summarize session history while retaining core working memory. For large codebases, use subagents in .claude/agents/ to handle background tasks like running test suites or scanning for linting errors without polluting your primary chat context.

Why it matters: Active context management keeps Claude Code fast, lowers token costs, and stops old conversation history from degrading response accuracy.


# Compact conversation history within an active CLI session
/compact

# Clear conversational history entirely for a new subtask
/clear

6. Open a Pull Request and Review the Diff

Once all batches complete, command Claude Code to analyze your branch against your primary repository branch. Have the agent generate a summarized pull request title, description, and list of changed modules, then open the PR using git commands.

Why it matters: Generating structured pull requests gives your human team full visibility into automated edits before merging code into production.


Compare our current refactor branch against main. 
Summarize all converted route handlers, list test results, and open a GitHub pull request titled "refactor(api): convert controllers from callbacks to async/await"

Real Example: Legacy Express API Migration

A tech lead at a software firm needed to migrate a legacy Express backend containing 60 controller files from callback patterns to modern async/await syntax. Here is how they completed the refactor safely:

Project Memory Config (CLAUDE.md)

The team established a lean project configuration file specifying test frameworks, linting rules, and forbidden file paths (e.g., database models).

Planning Execution

"Scan /src/controllers and identify all 60 callback instances. Group them into six logical batches of ten files each. Draft the step-by-step migration path."

Handling a Test Failure

During Batch 3, an auth middleware test failed due to an unhandled rejection error. Instead of reverting manually, the tech lead prompted:

"The test auth.test.js failed with an unhandled promise rejection on line 42. Analyze the stack trace, fix the error in /src/controllers/user.js, re-run the tests, and verify green status."

Claude Code identified a missing try/catch wrapper, updated the handler, ran npm test, verified passing status, and committed the changes.

Final PR Delivery

The agent opened a clean pull request detailing changes across 60 files, citing zero breaking API changes and 100 percent passing unit tests. The entire migration took 40 minutes.

Common Mistakes

  1. Granting broad permissions on primary branches: Running Claude Code in auto-accept mode directly on main or master can result in unverified commits that break production deployments. Always use working feature branches.

  2. Executing giant single-prompt refactors: Asking the agent to modify 50 files in one turn causes lost context, missed syntax errors, and huge, unreviewable diffs. Break large refactors into small, committed batches.

  3. Skipping the project memory file: Omitting a CLAUDE.md file forces Claude Code to guess build scripts, test suites, and formatting standards every single session, wasting tokens and introducing inconsistent code styles.

Take It Further

Add custom post-action hooks inside your .claude/settings.json file. Configure a hook that automatically executes your linter or code formatter (like Prettier or ESLint) every time Claude Code edits a file. This ensures every automated edit matches your repository styling rules before tests run.

Summary and Call to Action

You can manage large codebases agentically without sacrificing safety by pairing strict permission settings with project memory files and small git-backed iterations. Install Claude Code, draft your initial CLAUDE.md file today, and run your next migration on a clean working branch.