Refactoring Existing Code
Refactoring is the practice of restructuring existing code without changing its external behavior. It is essential for keeping a codebase maintainable, but it can be tedious and error-prone when done manually. Claude Code makes refactoring faster and safer by understanding the full context of your code and applying changes consistently across multiple files.
In this lesson, you will learn how to use Claude Code for various types of refactoring, from simple syntax updates to significant architecture changes.
Why Refactoring with Claude Code Works Well
Refactoring is a perfect task for Claude Code because:
- It requires reading many files -- Claude Code can scan your entire codebase to find every place a change needs to be applied
- Consistency matters -- Claude Code applies the same transformation pattern everywhere, not just in one file
- It is mechanical but error-prone -- Humans make mistakes on repetitive changes; Claude Code does not get bored or distracted
- Context is critical -- Claude Code understands your import paths, type definitions, and naming conventions
Simple Refactoring: Syntax and Pattern Updates
Converting Class Components to Function Components
If you have legacy React class components, Claude Code can modernize them:
> Convert src/components/UserDashboard.tsx from a class component
to a function component with hooks
Claude Code reads the class component, identifies the state, lifecycle methods, and class methods, then rewrites it using useState, useEffect, and regular functions.
Before:
class UserDashboard extends React.Component {
state = { users: [], loading: true };
componentDidMount() {
this.fetchUsers();
}
async fetchUsers() {
const response = await fetch('/api/users');
const users = await response.json();
this.setState({ users, loading: false });
}
render() {
if (this.state.loading) return <Spinner />;
return (
<div>
{this.state.users.map(user => (
<UserCard key={user.id} user={user} />
))}
</div>
);
}
}
After:
function UserDashboard() {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchUsers() {
const response = await fetch('/api/users');
const users = await response.json();
setUsers(users);
setLoading(false);
}
fetchUsers();
}, []);
if (loading) return <Spinner />;
return (
<div>
{users.map(user => (
<UserCard key={user.id} user={user} />
))}
</div>
);
}
Updating Variable Declarations
> Replace all var declarations with const or let throughout the
src/ directory, using const where the variable isn't reassigned
Claude Code will search for var declarations, analyze whether each variable is reassigned, and choose const or let appropriately. It does not just do a blind find-and-replace -- it understands the semantics.
Modernizing Syntax
> Convert all callback-style async code in src/lib/ to use
async/await instead of .then() chains
> Replace all string concatenation with template literals
in the components directory
Extracting Functions and Components
As code grows, functions and components become too large. Claude Code helps you extract pieces into reusable units.
Extracting a Utility Function
> The validation logic in src/components/CheckoutForm.tsx (lines 45-89)
is complex and used in multiple forms. Extract it into a separate
utility at src/utils/validation.ts
Claude Code will:
- Read the checkout form component
- Identify the validation logic
- Create a new utility file with the extracted function
- Update the original component to import from the new utility
- Search for other files that duplicate similar validation and update them too
Extracting a Component
> The product card markup in src/app/products/page.tsx is repeated
three times with slight variations. Extract it into a reusable
ProductCard component in src/components/ProductCard.tsx
Claude Code identifies the common elements, creates a component with props for the variations, and replaces all three instances with the new component.
Extracting a Custom Hook
> The data fetching logic in ProductList, UserList, and OrderList
components all follow the same pattern. Extract a reusable
useFetchList custom hook.
Claude Code reads all three components, identifies the shared pattern, creates the hook, and refactors all three components to use it:
Claude Code: I can see all three components follow this pattern:
- useState for data, loading, and error
- useEffect to fetch from an API endpoint
- Error handling with try/catch
- Loading and error state rendering
I'll create a useFetchList hook that captures this pattern.
[Creates src/hooks/useFetchList.ts]
[Edits src/components/ProductList.tsx]
Replaced 25 lines of fetch logic with: const { data, loading, error } = useFetchList('/api/products')
[Edits src/components/UserList.tsx]
Replaced 28 lines of fetch logic with: const { data, loading, error } = useFetchList('/api/users')
[Edits src/components/OrderList.tsx]
Replaced 22 lines of fetch logic with: const { data, loading, error } = useFetchList('/api/orders')
Renaming Across Files
Renaming a function, component, or variable in one file is easy. The hard part is updating every file that references it. Claude Code handles this automatically.
> Rename the UserCard component to UserProfileCard everywhere
in the codebase
Claude Code will:
- Find the component definition
- Search for every import and usage across all files
- Update the filename (if the file is named after the component)
- Update all imports
- Update all JSX usages
- Update any test files that reference it
Claude Code: I'll rename UserCard to UserProfileCard across the codebase.
[Renames src/components/UserCard.tsx to src/components/UserProfileCard.tsx]
[Edits src/components/UserProfileCard.tsx]
Renamed the component export
[Edits src/app/dashboard/page.tsx]
Updated import: UserCard → UserProfileCard
Updated usage: <UserCard /> → <UserProfileCard />
[Edits src/app/admin/users/page.tsx]
Updated import and 3 usages
[Edits src/__tests__/components/UserCard.test.tsx]
Renamed file to UserProfileCard.test.tsx
Updated all references in the test file
Found and updated 4 files total.
Renaming API Routes
> Rename the /api/posts endpoint to /api/articles and update all
fetch calls throughout the app
Claude Code renames the route file, updates all API calls, and updates any related types or constants.
Architecture Changes
Claude Code can handle larger refactoring efforts that change how your application is structured.
Moving to a New Data Fetching Pattern
> Convert the products page from client-side fetching with useEffect
to server-side fetching using Next.js server components
Claude Code understands the architectural difference and will:
- Convert the component from
"use client"to a server component - Replace
useStateanduseEffectwith a directasyncfunction - Move the fetch call to the component body
- Extract any interactive parts into separate client components
- Update the types if needed
Adding TypeScript to a JavaScript Project
> Convert src/utils/helpers.js to TypeScript. Infer types from the
existing usage throughout the codebase.
Claude Code reads how the functions are called across the codebase to infer accurate types, rather than just adding any everywhere.
Restructuring Directory Layout
> Move from a flat component structure to a feature-based structure.
Group components by feature: auth, dashboard, products, shared.
Update all imports.
Claude Code will:
- Create the new directory structure
- Move files to their new locations
- Update every import path across the entire codebase
- Verify no imports are broken
Code Style Improvements
Sometimes you want to bring code in line with the conventions used elsewhere in the codebase:
> Make the error handling in src/app/api/ consistent. Some routes
use try/catch, some use .catch(), and some don't handle errors
at all. Standardize on try/catch with proper error responses.
Claude Code reads all your API routes, identifies the inconsistencies, and applies a consistent error handling pattern:
Claude Code: I found 12 API routes. Here's the current state:
- 5 use try/catch (good)
- 3 use .catch() chains
- 4 have no error handling
I'll standardize all 12 routes to use try/catch with your existing
error response format.
[Edits 7 files to add/update error handling]
Matching Existing Patterns
> The components in src/components/dashboard/ don't follow the same
patterns as the rest of the app. They use inline styles instead of
Tailwind, and they don't use the existing Button and Input
components. Refactor them to match the existing conventions.
Claude Code reads the existing conventions from other components and applies them to the dashboard components.
Safety: Preserving Behavior
The cardinal rule of refactoring is: do not change behavior, only structure. Claude Code helps maintain this by:
Understanding Intent
When you say "refactor," Claude Code knows to preserve the external behavior. It will not add features or change logic unless you ask.
Running Tests After Refactoring
Ask Claude Code to verify its refactoring did not break anything:
> Refactor the auth module to use async/await instead of callbacks,
then run the tests to make sure everything still passes
Incremental Refactoring
For large refactoring efforts, work in stages:
> Let's refactor the payment module in three steps. First, extract
the Stripe-specific code into a separate adapter. Don't change
any behavior yet, just move the code.
After verifying that works:
> Good, tests pass. Now create an interface that the Stripe adapter
implements, so we can swap in other payment providers later.
And finally:
> Now update the rest of the codebase to use the interface instead
of importing Stripe functions directly.
Before and After: A Complete Example
Let us look at a complete refactoring example. Suppose you have a monolithic API route:
> The file src/app/api/orders/route.ts is 300 lines long and does
everything: validation, database queries, email sending, and Stripe
charges. Break it into separate modules with clear responsibilities
while keeping the same API behavior.
Claude Code will restructure it:
Claude Code: I'll break this into focused modules following the single
responsibility principle.
[Creates src/lib/orders/validation.ts]
- validateOrderInput() - schema validation with Zod
- validateInventory() - checks stock availability
[Creates src/lib/orders/repository.ts]
- createOrder() - database insert
- getOrderById() - database query
- updateOrderStatus() - status transitions
[Creates src/lib/orders/payment.ts]
- chargeOrder() - Stripe payment processing
- refundOrder() - Stripe refund handling
[Creates src/lib/orders/notifications.ts]
- sendOrderConfirmation() - email to customer
- notifyWarehouse() - internal notification
[Edits src/app/api/orders/route.ts]
- Now 40 lines: imports modules, orchestrates the flow
- Same API contract: same request/response format
- Same error codes and status codes
[Runs: npm test -- orders]
All 14 order tests passing.
The API route went from 300 lines to 40 lines, the logic is organized into clear modules, and all tests still pass.
What You Learned
In this lesson, you learned how to use Claude Code for refactoring:
- Simple syntax updates like replacing
varwithconst/letor modernizing async code - Extracting reusable functions, components, and custom hooks from existing code
- Renaming symbols across the entire codebase with automatic import updates
- Architecture changes like converting data fetching patterns or adding TypeScript
- Code style normalization to match existing project conventions
- Preserving behavior by running tests after refactoring and working incrementally
- Breaking monolithic files into focused, well-organized modules
In the next lesson, you will learn how Claude Code helps you write, run, and fix tests.
Questionário
Discussion
Sign in to join the discussion.

