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 device, which means it might learn recordsdata, write adjustments, create new modules, and execute shell instructions autonomously inside a single session. That energy makes a structured workflow important slightly than non-obligatory. With out one, you danger 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.
Methods 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/<title>to create a clear rollback level. - Immediate Claude Code to learn all goal recordsdata and map each dependency, explicitly forbidding modifications.
- Confirm no recordsdata have been modified throughout evaluation by working
git diff --name-only. - Request a step-by-step refactoring plan together with actual file adjustments, new exports, and import rewiring.
- Evaluate and approve (or modify) the proposed plan earlier than any execution begins.
- Execute adjustments one step at a time, committing after every step and working
npm take a look atto 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 Completely different
Chat-based AI assistants produce code snippets that builders paste by hand into their editors. Claude Code does one thing essentially completely different: it operates immediately on the filesystem. It reads supply recordsdata, edits them in place, creates new recordsdata, runs take a look at suites, and chains these actions collectively. You may configure its default habits and approval granularity — test Anthropic’s Claude Code documentation for the --approval or equal flags in your put in model. This agentic habits makes it highly effective for refactoring. It additionally makes undisciplined use harmful, as a result of the device will fortunately chain collectively damaging adjustments earlier than you perceive what occurred.
This agentic habits makes it highly effective for refactoring. It additionally makes undisciplined use harmful, as a result of the device 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 may 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 handbook 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 switch any file till it has first mapped the whole dependency graph of the refactoring goal. This implies studying all related recordsdata, figuring out each import, export, shared fixed, aspect 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 provides permission to start out modifying instantly. With out a full learn go, the device 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 Section 1, Claude Code analyzes the goal recordsdata and produces a dependency report with out modifying something. In Section 2, the device proposes a step-by-step refactoring plan that the developer evaluations, adjusts, and approves. Execution and verification come final: Section 3 applies adjustments incrementally, with take a look at 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
Stipulations
Earlier than beginning, affirm 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 verify. Shell execution permissions have to be enabled through theallowedToolsconfiguration in your Claude Code settings file (sometimes~/.claude/settings.json) for Claude Code to run instructions likenpm take a look atin your behalf. - Git is initialized in your venture root.
- Your venture has a
bundle.jsonwith Specific and Jest as dependencies. Runnpm record 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 characteristic. 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 `assessments/` could also be up to date solely to mirror 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 authorised.
- Commit after every logical refactoring step with a descriptive message.
- Run `npm take a look at` 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 should not enforced by the device itself — confirm compliance after every step utilizing git diff --name-only. The READ-ONLY declaration for middleware and config recordsdata indicators that cascading adjustments exterior the meant scope are undesirable, however all the time affirm through git diff that no out-of-scope recordsdata have been touched.
If a scoped file path doesn’t exist (e.g., as a result of a typo), Claude Code will report it as lacking slightly than silently skipping it — confirm paths earlier than beginning.
Selecting a Actual Refactoring Goal
The instance used all through this text is a standard situation: an Specific.js route handler that has gathered inline validation, information transformation, and response formatting logic in a single file. The objective 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 comprise:
module.exports = { TAX_RATE: 0.08, DISCOUNT_THRESHOLD: 100, DISCOUNT_RATE: 0.1 };
The route handler additionally imports from src/config/database.js, which offers db.orders.create(). That file’s implementation depends upon your database setup and is exterior 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 { objects, customerEmail } = req.physique;
if (!objects || !Array.isArray(objects) || objects.size === 0) {
return res.standing(400).json({ error: 'Gadgets have to be a non-empty array' });
}
if (!customerEmail || !/^[^s@]+@[^s@]+.[^s@]+$/.take a look at(customerEmail)) {
return res.standing(400).json({ error: 'Legitimate e-mail is required' });
}
for (let index = 0; index < objects.size; index++) {
const merchandise = objects[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 = objects.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' });
}
attempt {
const order = await db.orders.create({ objects, 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 obligations are tangled collectively on this file. The validation logic, the worth calculation, and the persistence/response dealing with all dwell in a single operate physique.
Section 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 will not be redundant. With out it, agentic habits can lead the device to start out “helpfully” restructuring code as a part of its evaluation. The CLAUDE.md guidelines reinforce this, however the prompt-level constraint offers a second layer of safety. Each layers are advisable, however neither is a assured exhausting cease. CLAUDE.md establishes venture intent; the prompt-level instruction reinforces it per request. At all times confirm through git diff earlier than committing.
After working this immediate, confirm no recordsdata have 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 anticipate; precise format varies by session and mannequin model:
## Dependency Evaluation: src/routes/orders.js
### Direct Imports
- `specific` (exterior bundle)
- `../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`
### Aspect Results
- No negative effects past commonplace Specific router instantiation.
### Secure Refactoring Boundaries
- Validation logic could be extracted with out affecting different recordsdata
- Worth calculation could 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 protected 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 slightly 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.
Section 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.
Primarily based 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. Embody the precise file adjustments, new exports, and import rewiring. Don't make any adjustments but.
The device responds with a numbered plan:
## Proposed Refactoring Plan
1. Create `src/utils/validators.js` with three exported features:
- `validateItems(objects)` — 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')`
- Substitute inline validation block (strains 9–24) with a name to `validateOrderPayload`
- No adjustments to cost calculation or persistence logic on this step
3. Replace `assessments/routes/orders.take a look at.js`:
- Present validation take a look at circumstances ought to nonetheless go (they take a look at through HTTP)
- Add new unit assessments 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 fastidiously. If the device proposed adjustments to recordsdata marked READ-ONLY in CLAUDE.md, that might be a purple flag price pushing again on. On this case, the plan respects the boundaries.
Setting Checkpoints and Rollback Factors
Earlier than approving execution, guarantee you might be on a characteristic department with a clear working tree. Run the next to arrange:
git checkout -b refactor/extract-validators
git standing
If the working tree will not be clear, run git stash or commit pending adjustments earlier than beginning the refactoring session.
Instructing Claude Code to commit after every logical step offers rollback factors. If step 2 breaks assessments, 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 device makes adjustments sooner than you may overview diffs.
The plan is now authorised; Section 3 turns it into working code.
Section 3: Execute and Confirm
Executing the Refactoring Step by Step
With the plan authorised, execution begins with step 1: creating the brand new validator module.
'use strict';
operate validateItems(objects) {
if (!objects || !Array.isArray(objects) || objects.size === 0) {
return { legitimate: false, error: 'Gadgets have to be a non-empty array' };
}
for (let index = 0; index < objects.size; index++) {
const merchandise = objects[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@]+$/.take a look at(e-mail)) {
return { legitimate: false, error: 'Legitimate e-mail is required' };
}
return { legitimate: true, error: null };
}
operate validateOrderPayload(physique) {
const itemsResult = validateItems(physique && physique.objects);
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 slightly than amassing all errors. This matches the habits 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 might be undefined; the null-safe entry through physique && physique.objects handles that case.
Error messages reference solely the merchandise index, not the uncooked merchandise information, to keep away from leaking doubtlessly delicate fields to purchasers.
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 { objects, customerEmail } = req.physique;
const subtotal = objects.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' });
}
attempt {
const order = await db.orders.create({ objects, 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 important in manufacturing. An unhandled rejection from db.orders.create() will crash the Specific course of on Node.js variations earlier than 15, or depart 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.
Working Exams and Catching Regressions
Working shell instructions requires the allowedTools permission to be configured in your Claude Code settings. Affirm that shell execution is permitted earlier than counting on automated take a look at runs.
After every step, the device ought to run the take a look at suite. The immediate is direct:
Run npm take a look at. If any assessments fail, present me the failure output and counsel a repair. Don't apply the repair till I approve.
This assumes your bundle.json features a "take a look at": "jest" script and that Jest is configured to seek out your take a look at recordsdata.
If a take a look at fails as a result of it immediately asserts on the error message format and the refactored validators modified the wording, the device flags it. You may then approve or reject the proposed take a look at replace, sustaining management over what constitutes a suitable behavioral change versus an unintended regression.
Closing Evaluate and Cleanup
In any case steps full and assessments go, a closing overview immediate catches free ends: “Evaluate all recordsdata modified on this session. Test for orphaned imports, unused variables, inconsistent naming, and any references to the outdated 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 out a planning part, the device will usually suggest broader adjustments than meant as a result of it lacks context about which adjustments you think about protected. The planning part is the place you apply that judgment.
Learn Section
- ☐ Configure
CLAUDE.md(uppercase, case-sensitive) with refactoring scope and read-only declarations - ☐ Establish the refactoring goal and create a characteristic department (
git checkout -b refactor/<title>) - ☐ Affirm Claude Code shell execution permissions are enabled through
allowedTools - ☐ Immediate Claude Code to learn and map all dependencies. Explicitly instruct it to not modify recordsdata.
- ☐ Confirm no recordsdata have been modified (
git diff --name-onlyneeds to be empty) - ☐ Evaluate the dependency report for shared state and negative effects
Plan Section
- ☐ Request a step-by-step refactoring plan
- ☐ Approve or modify the plan earlier than continuing
Execute Section
- ☐ Execute adjustments one step at a time with git commits between steps
- ☐ Run assessments after every step
- ☐ Repair any regressions earlier than continuing to the following step
- ☐ Closing 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 Methods to Keep away from Them
Letting Claude Code Change Recordsdata Throughout the Learn Section
You’ll discover unintended edits when an evaluation request will get interpreted as an invite to “repair” issues. You stop this by combining two layers of constraint: the CLAUDE.md rule (“Don’t modify any file till a refactoring plan has been explicitly authorised”) and the prompt-level instruction (“Don’t modify any recordsdata”). Each layers are advisable, however neither is a assured exhausting cease. CLAUDE.md establishes venture intent; the prompt-level instruction reinforces it per request. At all times confirm through git diff earlier than committing.
Refactoring Too Many Recordsdata at As soon as
Context window limits are an actual constraint. When the device 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 entire token depend of all recordsdata in scope and hold 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. Once 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 Section
Leaping from dependency evaluation on to execution prevents you from catching scope creep, rejecting pointless adjustments, or reordering steps for security. With out a planning part, the device will usually suggest broader adjustments than meant as a result of it lacks context about which adjustments you think about protected. 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.
Supply hyperlink


