Refactoring Code and Working with Tests
Refactoring and testing are two of the most impactful but time-consuming parts of software development. Developers often avoid refactoring because the risk of breaking something is high, and writing comprehensive tests is tedious. Claude Code changes both of these equations dramatically.
In this lesson, you will learn how to use Claude Code for safe, effective refactoring and for writing, running, and maintaining tests.
What You Will Learn
- How to refactor code safely with Claude Code
- Common refactoring patterns Claude Code handles well
- How to write tests using Claude Code
- Running and debugging tests within Claude Code
- Test-driven development with Claude Code
- Maintaining and updating test suites
Refactoring with Claude Code
Why Claude Code Is Great at Refactoring
Refactoring often requires understanding how a change in one file affects many others. This is exactly where Claude Code excels because it can:
- Read the entire scope of a refactoring at once
- Track all references to a function, class, or variable
- Make coordinated changes across many files
- Run tests after each change to verify nothing broke
Simple Refactoring Examples
Renaming across a codebase:
Rename the function getUserData to fetchUserProfile everywhere
it is used in the project. Update all imports, calls, and tests.
Claude Code finds every reference, updates them all, and ensures imports are correct. This is more reliable than an IDE rename because Claude Code also updates string references, comments, and documentation.
Extracting a function:
The handleSubmit function in src/components/OrderForm.tsx is too long.
Extract the validation logic into a separate validateOrder function
in src/utils/validation.ts.
Claude Code extracts the logic, creates the new function with proper typing, updates the original file to use the new function, and adds the necessary import.
Converting patterns:
Convert all callback-based functions in src/services/database.ts
to use async/await instead.
Claude Code reads each function, converts the callback patterns to async/await, updates error handling from try/catch around callbacks to proper async error handling, and adjusts return types.
Complex Refactoring
Restructuring modules:
The src/utils/ folder has 30 files that are hard to navigate.
Reorganize it into logical subfolders:
- src/utils/formatting/ for string and date formatters
- src/utils/validation/ for validators
- src/utils/api/ for API helpers
Update all imports across the project.
Claude Code moves files, creates the directory structure, and updates every import statement throughout the codebase. It even updates dynamic imports and path references in configuration files.
Changing data models:
Refactor the User type to split the address fields into a separate
Address type. Update all code that reads or writes user addresses
to use the new nested structure.
Claude Code updates the type definitions, modifies database queries or API calls that reference the old flat structure, updates frontend components that display address data, and adjusts tests.
Removing dead code:
Find and remove all unused exports, functions, and variables
in the src/utils/ directory. Do not remove anything that is
used by tests.
Claude Code analyzes imports and usage patterns to identify truly dead code, then removes it safely.
The Refactoring Workflow
For any significant refactoring, follow this workflow:
- Explain your goal to Claude Code before asking for changes
- Ask for a plan before execution: "How would you approach this refactoring?"
- Review the plan and adjust if needed
- Execute in stages rather than all at once
- Run tests after each stage to catch issues early
- Review the diff before committing
Working with Tests
Writing Tests with Claude Code
Claude Code is excellent at writing tests because it understands both your source code and your testing patterns.
Writing tests for existing code:
Write unit tests for the calculateDiscount function in
src/services/pricing.ts. Cover normal cases, edge cases,
and error conditions.
Claude Code reads the function, understands all the code paths, and writes tests that cover:
- Normal inputs with expected outputs
- Boundary values (zero, maximum values, negative numbers)
- Error conditions (invalid inputs, null values)
- Edge cases specific to the business logic
Matching your test style:
Write tests for the UserService class. Follow the same testing
patterns used in src/__tests__/ProductService.test.ts.
By referencing an existing test file, Claude Code matches your assertion style, mocking patterns, setup/teardown approach, and naming conventions.
Generating test data:
Create a test fixtures file for the Order model with
realistic sample data covering different order statuses,
payment methods, and edge cases.
Claude Code generates realistic test data that covers the variations you need.
Running Tests
Claude Code can run your tests and respond to the results:
Run the test suite and fix any failures.
Claude Code executes your test command (it figures out whether to use npm test, pytest, cargo test, etc.), reads the output, identifies failures, and fixes them.
For more targeted testing:
Run only the tests related to authentication
and show me the results.
Claude Code finds and runs the relevant test files.
Test-Driven Development (TDD) with Claude Code
Claude Code supports a TDD workflow naturally:
Step 1: Write the test first
Write a test for a function called parseCSV that takes a CSV
string and returns an array of objects. The first row should
be used as headers. Test with normal data, empty input, and
malformed CSV.
Claude Code writes the test file with all the assertions.
Step 2: Run the test (it should fail)
Run the parseCSV tests. They should fail since the
function does not exist yet.
Claude Code runs the tests and confirms they fail with "function not found" or similar errors.
Step 3: Write the implementation
Now implement the parseCSV function to make all the tests pass.
Claude Code writes the implementation, targeting the exact behavior the tests expect.
Step 4: Verify
Run the tests again to confirm they all pass.
This TDD loop is fast and reliable with Claude Code driving each step.
Maintaining Tests
As your codebase evolves, tests need updating too:
Updating tests after feature changes:
I changed the login endpoint to require a CAPTCHA token.
Update all the authentication tests to include this new
required field.
Claude Code finds all affected tests and adds the necessary changes consistently.
Improving test coverage:
What code paths in src/services/payment.ts are not covered
by the existing tests? Write tests for the uncovered paths.
Claude Code analyzes the source code and existing tests to identify gaps, then writes tests to fill them.
Fixing flaky tests:
The test "should handle concurrent updates" in
src/__tests__/database.test.ts sometimes passes and
sometimes fails. Diagnose and fix the flakiness.
Claude Code examines the test for common causes of flakiness: race conditions, timing dependencies, shared state, or external service dependencies, and proposes a stable fix.
Combining Refactoring and Testing
The most powerful workflow combines both:
Refactor the PaymentService to use the Strategy pattern for
different payment methods. After refactoring, make sure all
existing tests still pass, and add new tests for the
Strategy implementation.
Claude Code:
- Reads the existing code and tests
- Plans the refactoring
- Makes the changes
- Runs existing tests to verify nothing broke
- Writes new tests for the new pattern
- Verifies everything passes
This combined approach gives you the confidence to refactor aggressively, knowing that tests catch any regressions.
Key Takeaways
- Claude Code excels at refactoring because it can track references across files, make coordinated changes, and verify with tests
- For complex refactoring, ask Claude Code for a plan first, then execute in stages with tests between each stage
- Claude Code writes comprehensive tests by reading your source code and understanding all code paths, including edge cases
- Reference existing test files so Claude Code matches your testing patterns and conventions
- Test-driven development works naturally with Claude Code: write tests first, verify they fail, implement the code, verify they pass
- Combine refactoring and testing for the safest workflow: refactor, run existing tests, write new tests, verify

