Refactoring JavaScript with Claude Code calls for extra self-discipline than refactoring by hand or with a chat-based assistant. Claude Code operates as an agentic CLI software, which means it might probably learn recordsdata, write adjustments, create new modules, and execute shell instructions autonomously inside a single session. That energy makes a structured workflow important moderately than elective. With out one, you threat scattered edits throughout recordsdata, damaged imports, wasted context tokens, and regressions that solely floor after the session ends. This text walks by way of a concrete, repeatable sample for utilizing Claude Code to decompose a monolithic Specific.js route handler into clear, separated modules, step-by-step, with full prompts and code.
The way to Refactor Code with Claude Code
- Configure a
CLAUDE.mdfile declaring in-scope and read-only recordsdata, session guidelines, and venture context. - Department your repository with
git checkout -b refactor/to create a clear rollback level. - Immediate Claude Code to learn all goal recordsdata and map each dependency, explicitly forbidding modifications.
- Confirm no recordsdata had been modified throughout evaluation by working
git diff --name-only. - Request a step-by-step refactoring plan together with precise file adjustments, new exports, and import rewiring.
- Evaluate and approve (or regulate) the proposed plan earlier than any execution begins.
- Execute adjustments one step at a time, committing after every step and working
npm checkto catch regressions. - Sweep all modified recordsdata for orphaned imports, unused variables, and useless code earlier than submitting your PR.
Desk of Contents
Why Refactoring with Claude Code Is Totally different
Chat-based AI assistants produce code snippets that builders paste by hand into their editors. Claude Code does one thing basically totally different: it operates immediately on the filesystem. It reads supply recordsdata, edits them in place, creates new recordsdata, runs check suites, and chains these actions collectively. You possibly can configure its default conduct and approval granularity — test Anthropic’s Claude Code documentation for the --approval or equal flags to your put in model. This agentic conduct makes it highly effective for refactoring. It additionally makes undisciplined use harmful, as a result of the software will fortunately chain collectively damaging adjustments earlier than you perceive what occurred.
This agentic conduct makes it highly effective for refactoring. It additionally makes undisciplined use harmful, as a result of the software will fortunately chain collectively damaging adjustments earlier than you perceive what occurred.
When a developer sorts “refactor this file” with out constraints, an unconstrained immediate could cause Claude Code to rewrite the goal file, replace imports in dependent recordsdata, and rename exports in a single motion, all earlier than the developer has reviewed the dependency graph. In guide refactoring, the developer naturally builds a psychological mannequin of the code earlier than touching something. With out express instruction, Claude Code skips that step. A structured workflow restores that deliberate sequencing, guaranteeing that studying all the time precedes writing.
The Learn-Earlier than-Altering Sample Defined
What Is the Learn-Earlier than-Altering Sample?
By no means permit Claude Code to change any file till it has first mapped the entire dependency graph of the refactoring goal. This implies studying all related recordsdata, figuring out each import, export, shared fixed, facet impact, and cross-file reference earlier than a single line adjustments.
The widespread anti-pattern is prompting with one thing like “Extract the validation logic from orders.js right into a separate module.” That immediate offers permission to start out enhancing instantly. With no full learn move, the software can miss a shared variable referenced three recordsdata away. The read-before-changing sample treats evaluation and modification as strictly separate phases, which aligns with how Claude Code manages context: the extra full its understanding of the codebase earlier than appearing, the extra constant its adjustments.
The Three Phases at a Look
The workflow divides into three distinct phases. Throughout Part 1, Claude Code analyzes the goal recordsdata and produces a dependency report with out enhancing something. In Part 2, the software proposes a step-by-step refactoring plan that the developer evaluations, adjusts, and approves. Execution and verification come final: Part 3 applies adjustments incrementally, with check runs and git commits between every step. The sections beneath stroll by way of every part utilizing an actual JavaScript refactoring instance.
Setting Up Your Refactoring Session
Conditions
Earlier than beginning, verify the next:
- Node.js 20.x is put in (
node --versionought to returnv20.x.x). - Claude Code CLI is put in and authenticated. Run
claude --versionto substantiate. Shell execution permissions have to be enabled by way of theallowedToolsconfiguration in your Claude Code settings file (usually~/.claude/settings.json) for Claude Code to run instructions likenpm checkin your behalf. - Git is initialized in your venture root.
- Your venture has a
package deal.jsonwith Specific and Jest as dependencies. Runnpm listing specific jestto confirm variations. The instance beneath assumes Specific 4.18.x and Jest 29.x; Specific 5.x launched breaking adjustments to async error dealing with, so confirm your model matches.
Undertaking Construction and CLAUDE.md Configuration
Claude Code reads a CLAUDE.md file on the venture root (the filename is case-sensitive; use uppercase CLAUDE.md on Linux and macOS) to choose up session-level directions. Confirm your Claude Code model helps this function. For refactoring work, this file ought to declare which recordsdata are in scope, that are off-limits, and what guidelines Claude Code should comply with throughout evaluation.
# CLAUDE.md — Refactoring Session Configuration
## Scope
- Recordsdata in `src/routes/` and `src/utils/` are in scope for refactoring.
- Recordsdata in `src/middleware/` and `src/config/` are READ-ONLY. Don't modify.
- Take a look at recordsdata in `checks/` could also be up to date solely to replicate moved imports.
## Guidelines
- At all times learn and map dependencies earlier than proposing adjustments.
- Don't modify any file till a refactoring plan has been explicitly permitted.
- Commit after every logical refactoring step with a descriptive message.
- Run `npm check` after each file modification.
## Undertaking Context
- Runtime: Node.js 20
- Framework: Specific.js 4.18
- Take a look at runner: Jest 29
This configuration declares scope boundaries that Claude Code is instructed to respect. These aren’t enforced by the software itself — confirm compliance after every step utilizing git diff --name-only. The READ-ONLY declaration for middleware and config recordsdata indicators that cascading adjustments outdoors the supposed scope are undesirable, however all the time verify by way of git diff that no out-of-scope recordsdata had been touched.
If a scoped file path doesn’t exist (e.g., on account of a typo), Claude Code will report it as lacking moderately than silently skipping it — confirm paths earlier than beginning.
Selecting a Actual Refactoring Goal
The instance used all through this text is a standard state of affairs: an Specific.js route handler that has accrued inline validation, information transformation, and response formatting logic in a single file. The aim is to extract the validation logic right into a devoted utility module.
First, guarantee your supporting recordsdata exist. The route handler imports from src/utils/constants.js, which ought to include:
module.exports = { TAX_RATE: 0.08, DISCOUNT_THRESHOLD: 100, DISCOUNT_RATE: 0.1 };
The route handler additionally imports from src/config/database.js, which gives db.orders.create(). That file’s implementation will depend on your database setup and is outdoors the refactoring scope.
Right here is the route handler to be refactored:
const specific = require('specific');
const router = specific.Router();
const db = require('../config/database');
const { TAX_RATE, DISCOUNT_THRESHOLD, DISCOUNT_RATE } = require('../utils/constants');
router.submit('/orders', async (req, res) => {
const { gadgets, customerEmail } = req.physique;
if (!gadgets || !Array.isArray(gadgets) || gadgets.size === 0) {
return res.standing(400).json({ error: 'Objects have to be a non-empty array' });
}
if (!customerEmail || !/^[^s@]+@[^s@]+.[^s@]+$/.check(customerEmail)) {
return res.standing(400).json({ error: 'Legitimate e mail is required' });
}
for (let index = 0; index < gadgets.size; index++) {
const merchandise = gadgets[index];
if (!merchandise.sku || !Quantity.isInteger(merchandise.amount) || merchandise.amount < 1) {
return res.standing(400).json({ error: `Merchandise at index ${index} has an invalid SKU or amount` });
}
if (typeof merchandise.worth !== 'quantity' || merchandise.worth < 0) {
return res.standing(400).json({ error: `Merchandise at index ${index} has an invalid worth` });
}
}
const subtotal = gadgets.scale back((sum, i) => sum + (i.worth * i.amount), 0);
const low cost = subtotal > DISCOUNT_THRESHOLD ? subtotal * DISCOUNT_RATE : 0;
const whole = (subtotal - low cost) * (1 + TAX_RATE);
if (!Quantity.isFinite(whole)) {
return res.standing(400).json({ error: 'Order whole couldn't be calculated' });
}
strive {
const order = await db.orders.create({ gadgets, customerEmail, subtotal, low cost, whole });
const roundedTotal = parseFloat(whole.toFixed(2));
res.standing(201).json({ orderId: order.id, whole: roundedTotal });
} catch (err) {
console.error('Order creation failed:', err);
res.standing(500).json({ error: 'Order creation failed' });
}
});
module.exports = router;
Three distinct tasks are tangled collectively on this file. The validation logic, the worth calculation, and the persistence/response dealing with all reside in a single operate physique.
Part 1: Learn and Map Dependencies
Prompting Claude Code to Analyze Earlier than Performing
The primary immediate have to be express about what to do and, critically, what to not do. Imprecise prompts result in untimely edits.
Learn src/routes/orders.js and all recordsdata it imports. Checklist each operate, variable, and module dependency. Establish which symbols are used throughout a number of recordsdata. Don't modify any recordsdata.
The “Don’t modify any recordsdata” instruction shouldn’t be redundant. With out it, agentic conduct can lead the software to start out “helpfully” restructuring code as a part of its evaluation. The CLAUDE.md guidelines reinforce this, however the prompt-level constraint gives a second layer of safety. Each layers are really helpful, however neither is a assured laborious cease. CLAUDE.md establishes venture intent; the prompt-level instruction reinforces it per request. At all times confirm by way of git diff earlier than committing.
After working this immediate, confirm no recordsdata had been modified:
git diff --name-only
Understanding the Dependency Report
Claude Code produces a dependency evaluation. The next is an illustrative instance of the form of output you may count on; precise format varies by session and mannequin model:
## Dependency Evaluation: src/routes/orders.js
### Direct Imports
- `specific` (exterior package deal)
- `../config/database` → exports: `db.orders.create()`, `db.orders.discover()`
- `../utils/constants` → exports: `TAX_RATE` (0.08), `DISCOUNT_THRESHOLD` (100), `DISCOUNT_RATE` (0.1)
### Symbols Outlined in This File
- `router` (Specific Router occasion) — exported as module.exports
- Inline validation logic (no named features, not reusable)
- Inline worth calculation (references TAX_RATE, DISCOUNT_THRESHOLD, DISCOUNT_RATE)
### Cross-File References
- `TAX_RATE`, `DISCOUNT_THRESHOLD`, and `DISCOUNT_RATE` are additionally imported by `src/routes/invoices.js`
- `db.orders.create()` is named solely from this file
- No different file imports from `src/routes/orders.js`
### Facet Results
- No unintended effects past normal Specific router instantiation.
### Secure Refactoring Boundaries
- Validation logic may be extracted with out affecting different recordsdata
- Worth calculation may be extracted; solely references constants from utils/constants
- Router setup and db name ought to stay on this file
This report reveals that the validation and worth calculation logic haven’t any exterior dependents, making them secure extraction targets. The cross-file reference to TAX_RATE, DISCOUNT_THRESHOLD, and DISCOUNT_RATE in invoices.js is essential: it confirms these constants ought to keep in utils/constants moderately than being moved.
With the dependency map full, you now have a transparent image of what can transfer safely — which feeds immediately into the planning part.
Part 2: Plan the Refactoring
Producing a Step-by-Step Refactoring Plan
With the dependency map in hand, the following immediate asks for a plan with out executing it.
Based mostly in your dependency evaluation, suggest a step-by-step refactoring plan to extract validation logic from src/routes/orders.js into a brand new src/utils/validators.js module. Embrace the precise file adjustments, new exports, and import rewiring. Don't make any adjustments but.
The software responds with a numbered plan:
## Proposed Refactoring Plan
1. Create `src/utils/validators.js` with three exported features:
- `validateItems(gadgets)` — returns ` null `
- `validateEmail(e mail)` — returns ` null `
- `validateOrderPayload(physique)` — orchestrator that calls each
2. Replace `src/routes/orders.js`:
- Add import: `const { validateOrderPayload } = require('../utils/validators')`
- Exchange inline validation block (strains 9–24) with a name to `validateOrderPayload`
- No adjustments to cost calculation or persistence logic on this step
3. Replace `checks/routes/orders.check.js`:
- Present validation check instances ought to nonetheless move (they check by way of HTTP)
- Add new unit checks for `validators.js` features immediately
4. No adjustments wanted to:
- `src/config/database.js` (read-only)
- `src/utils/constants.js` (no modification required)
- `src/routes/invoices.js` (no shared dependency affected)
Evaluate this plan rigorously. If the software proposed adjustments to recordsdata marked READ-ONLY in CLAUDE.md, that may be a pink flag value pushing again on. On this case, the plan respects the boundaries.
Setting Checkpoints and Rollback Factors
Earlier than approving execution, guarantee you’re on a function department with a clear working tree. Run the next to arrange:
git checkout -b refactor/extract-validators
git standing
If the working tree shouldn’t be clear, run git stash or commit pending adjustments earlier than beginning the refactoring session.
Instructing Claude Code to commit after every logical step gives rollback factors. If step 2 breaks checks, reverting to the commit from step 1 is trivial. This isn’t distinctive to AI-assisted refactoring, however it turns into extra essential when an agentic software makes adjustments sooner than you may overview diffs.
The plan is now permitted; Part 3 turns it into working code.
Part 3: Execute and Confirm
Executing the Refactoring Step by Step
With the plan permitted, execution begins with step 1: creating the brand new validator module.
'use strict';
operate validateItems(gadgets) {
if (!gadgets || !Array.isArray(gadgets) || gadgets.size === 0) {
return { legitimate: false, error: 'Objects have to be a non-empty array' };
}
for (let index = 0; index < gadgets.size; index++) {
const merchandise = gadgets[index];
if (!merchandise.sku) {
return { legitimate: false, error: `Merchandise at index ${index} is lacking a SKU` };
}
if (!Quantity.isInteger(merchandise.amount) || merchandise.amount < 1) {
return { legitimate: false, error: `Merchandise at index ${index} has an invalid amount` };
}
if (typeof merchandise.worth !== 'quantity' || merchandise.worth < 0) {
return { legitimate: false, error: `Merchandise at index ${index} has an invalid worth` };
}
}
return { legitimate: true, error: null };
}
operate validateEmail(e mail) {
if (!e mail || !/^[^s@]+@[^s@]+.[^s@]+$/.check(e mail)) {
return { legitimate: false, error: 'Legitimate e mail is required' };
}
return { legitimate: true, error: null };
}
operate validateOrderPayload(physique) {
const itemsResult = validateItems(physique && physique.gadgets);
if (!itemsResult.legitimate) return itemsResult;
return validateEmail(physique && physique.customerEmail);
}
module.exports = { validateItems, validateEmail, validateOrderPayload };
Word: validateOrderPayload makes use of short-circuit analysis — it returns on the primary validation failure moderately than amassing all errors. This matches the conduct of the unique inline code, which additionally returned on the primary failure. In case your Specific setup doesn’t embody body-parser middleware, the physique parameter could possibly be undefined; the null-safe entry by way of physique && physique.gadgets handles that case.
Error messages reference solely the merchandise index, not the uncooked merchandise information, to keep away from leaking probably delicate fields to shoppers.
The cleaned-up route handler now delegates validation:
'use strict';
const specific = require('specific');
const router = specific.Router();
const db = require('../config/database');
const { TAX_RATE, DISCOUNT_THRESHOLD, DISCOUNT_RATE } = require('../utils/constants');
const { validateOrderPayload } = require('../utils/validators');
router.submit('/orders', async (req, res) => {
const validation = validateOrderPayload(req.physique);
if (!validation.legitimate) {
return res.standing(400).json({ error: validation.error });
}
const { gadgets, customerEmail } = req.physique;
const subtotal = gadgets.scale back((sum, i) => sum + (i.worth * i.amount), 0);
const low cost = subtotal > DISCOUNT_THRESHOLD ? subtotal * DISCOUNT_RATE : 0;
const whole = (subtotal - low cost) * (1 + TAX_RATE);
if (!Quantity.isFinite(whole)) {
return res.standing(400).json({ error: 'Order whole couldn't be calculated' });
}
strive {
const order = await db.orders.create({ gadgets, customerEmail, subtotal, low cost, whole });
const roundedTotal = parseFloat(whole.toFixed(2));
res.standing(201).json({ orderId: order.id, whole: roundedTotal });
} catch (err) {
console.error('Order creation failed:', err);
res.standing(500).json({ error: 'Order creation failed' });
}
});
module.exports = router;
Error dealing with across the database name is crucial in manufacturing. An unhandled rejection from db.orders.create() will crash the Specific course of on Node.js variations earlier than 15, or go away the request hanging on later variations. The Quantity.isFinite guard ensures {that a} NaN or Infinity whole (which might come up from surprising enter combos) is caught earlier than reaching the database.
The route handler dropped from 27 strains contained in the callback to twenty strains, and the validation logic is now independently testable.
Operating Exams and Catching Regressions
Operating shell instructions requires the allowedTools permission to be configured in your Claude Code settings. Verify that shell execution is permitted earlier than counting on automated check runs.
After every step, the software ought to run the check suite. The immediate is direct:
Run npm check. If any checks fail, present me the failure output and counsel a repair. Don't apply the repair till I approve.
This assumes your package deal.json features a "check": "jest" script and that Jest is configured to search out your check recordsdata.
If a check fails as a result of it immediately asserts on the error message format and the refactored validators modified the wording, the software flags it. You possibly can then approve or reject the proposed check replace, sustaining management over what constitutes an appropriate behavioral change versus an unintended regression.
Ultimate Evaluate and Cleanup
In spite of everything steps full and checks move, a closing overview immediate catches unfastened ends: “Evaluate all recordsdata modified on this session. Examine for orphaned imports, unused variables, inconsistent naming, and any references to the previous inline validation code.” This sweep catches the form of particles that accumulates throughout multi-step refactoring, corresponding to a leftover remark referencing “inline validation” within the route handler or an unused require assertion.
The Full Refactoring Guidelines
With no planning part, the software will usually suggest broader adjustments than supposed as a result of it lacks context about which adjustments you contemplate secure. The planning part is the place you apply that judgment.
Learn Part
- ☐ Configure
CLAUDE.md(uppercase, case-sensitive) with refactoring scope and read-only declarations - ☐ Establish the refactoring goal and create a function department (
git checkout -b refactor/) - ☐ Verify Claude Code shell execution permissions are enabled by way of
allowedTools - ☐ Immediate Claude Code to learn and map all dependencies. Explicitly instruct it to not modify recordsdata.
- ☐ Confirm no recordsdata had been modified (
git diff --name-onlymust be empty) - ☐ Evaluate the dependency report for shared state and unintended effects
Plan Part
- ☐ Request a step-by-step refactoring plan
- ☐ Approve or regulate the plan earlier than continuing
Execute Part
- ☐ Execute adjustments one step at a time with git commits between steps
- ☐ Run checks after every step
- ☐ Repair any regressions earlier than continuing to the following step
- ☐ Ultimate overview: orphaned imports, naming consistency, useless code
- ☐ Squash or manage commits for PR submission (e.g.,
git rebase -i HEAD~Nthe place N is the variety of refactoring commits)
Widespread Pitfalls and The way to Keep away from Them
Letting Claude Code Change Recordsdata Through the Learn Part
You’ll discover unintended edits when an evaluation request will get interpreted as an invite to “repair” issues. You forestall this by combining two layers of constraint: the CLAUDE.md rule (“Don’t modify any file till a refactoring plan has been explicitly permitted”) and the prompt-level instruction (“Don’t modify any recordsdata”). Each layers are really helpful, however neither is a assured laborious cease. CLAUDE.md establishes venture intent; the prompt-level instruction reinforces it per request. At all times confirm by way of git diff earlier than committing.
Refactoring Too Many Recordsdata at As soon as
Context window limits are an actual constraint. When the software tracks adjustments throughout too many recordsdata concurrently, it produces inconsistent edits — updating an import in a single file however lacking the identical import in one other. As a tough heuristic, estimate the overall token depend of all recordsdata in scope and preserve it underneath 40% of your mannequin’s context restrict. In the event you can’t estimate tokens simply, cap a single session at 5 recordsdata or fewer. Whenever you discover missed imports or inconsistent edits, break up the work into sequential classes. Bigger refactors ought to comply with their very own read-map-plan-execute cycle per session.
Skipping the Planning Part
Leaping from dependency evaluation on to execution prevents you from catching scope creep, rejecting pointless adjustments, or reordering steps for security. With no planning part, the software will usually suggest broader adjustments than supposed as a result of it lacks context about which adjustments you contemplate secure. The planning part is the place you apply that judgment.
A structured workflow restores that deliberate sequencing, guaranteeing that studying all the time precedes writing.
