Simyl
simylflow
Course Home
Module 2: Technical Practices
Lesson 3 of 5
12 min

Refactoring

Improving code without changing what it does. The discipline that keeps codebases healthy.

1What Refactoring Is (and Isn't)

Refactoring is improving the internal structure of code without changing its external behavior.

That definition has two critical parts:

Improving structure: Making code easier to understand, modify, or extend. This includes better names, smaller functions, clearer organization, less duplication.

Without changing behavior: The code does exactly what it did before. Inputs produce the same outputs. Side effects are identical. Tests still pass.

Refactoring is not:

  • Rewriting from scratch
  • Adding features
  • Fixing bugs
  • Performance optimization (usually)

If you're changing what the code does, you're not refactoring—you're doing something else. Refactoring is specifically about structure, not behavior.

Refactoring is like cleaning your kitchen. The food still tastes the same. But cooking becomes easier, faster, and less frustrating.

2The Refactoring Catalog

Martin Fowler documented dozens of specific refactorings, each with:

  • A name (so teams can communicate)
  • A motivation (when to use it)
  • Mechanics (step-by-step how to do it safely)

Here are some you'll use constantly:

Extract Function: Take some code and put it in a new function with a clear name. This is probably the most common refactoring.

Rename: Change the name of a variable, function, or class to better express its purpose. Good names are surprisingly important.

Inline Function: The opposite of extract—replace a function call with its body. Use when a function doesn't add clarity.

Extract Variable: Take a complex expression and give it a name by assigning it to a variable.

Move Function: Move a function to a class where it makes more sense.

Replace Conditional with Polymorphism: Replace complex if/else or switch statements with object-oriented dispatch.

These aren't "advanced" refactorings. They're everyday tools. Learn them until they're automatic.

3Refactoring Safely

Refactoring is only safe when you have tests. Without tests, every change might break something, and you won't know until production.

The safety process:

  1. Ensure tests pass before starting
  2. Make one small change
  3. Run tests
  4. If tests pass, continue; if they fail, revert immediately
  5. Repeat

Small steps are key. Don't try to refactor five things at once. Extract one function, run tests, commit. Rename one variable, run tests, commit. This way, if something breaks, you know exactly what caused it.

Modern IDEs have automated refactorings that are safer than manual changes. "Rename" in your IDE updates all references. "Extract Function" creates the function and updates the call site. Use these tools.

The Boy Scout Rule: Leave the code better than you found it. When you touch code for any reason, make one small improvement. Over time, the codebase gets cleaner instead of dirtier.

No Tests = No Safety

Refactoring without tests is just changing code and hoping. It's not refactoring—it's gambling.

4When to Refactor

Refactor constantly. Refactoring isn't a separate phase or sprint. It's continuous.

Specific triggers to refactor:

Before adding a feature: If the current structure makes the feature hard to add, refactor first. Make the change easy, then make the easy change.

After making a test pass: This is the "refactor" in red-green-refactor. Clean up before moving to the next test.

When you notice a code smell: Duplicate code, long methods, unclear names, complex conditionals—these are signals that refactoring is needed.

During code review: If you see something you'd improve, either do it or note it for later.

When you have time: Have a "refactoring backlog" of known issues. When you have slack, pick something off the list.

Don't refactor when:

  • You don't have tests
  • You're about to ship (stabilize, don't change)
  • You don't understand what the code does (understand first)

5Technical Debt and Refactoring

Technical debt is the accumulated cost of taking shortcuts. Every time you say "I'll clean this up later" and don't, you add debt. Every time you copy-paste instead of extract, you add debt.

Debt accumulates interest. Messy code takes longer to change. Bugs hide in complexity. New team members struggle to understand. The cost of change increases.

Refactoring is how you pay down debt. Regular refactoring keeps debt manageable. Neglecting refactoring lets debt compound until the codebase becomes unworkable.

The key insight: Refactoring isn't a luxury. It's essential maintenance. You don't ask permission to pay down debt—you just do it as part of your work.

XP builds refactoring into the process. Every feature, every bug fix, every task includes time to leave the code better than you found it. You don't need a "refactoring sprint"—you need a refactoring habit.

Good Refactoring Practice

When fixing a bug, you notice duplicate code nearby. You fix the bug first (behavior change), then extract the duplication into a function (refactoring). Two commits: bug fix, then refactoring.

Refactoring Gone Wrong

While adding a feature, you decide to also refactor the authentication system, reorganize the folder structure, and update the database schema. You lose track of what changed, tests fail mysteriously, and you spend hours debugging.

Key Takeaways
  • Refactoring improves code structure without changing behavior
  • Tests are essential—refactoring without tests is just changing code and hoping
  • Take small steps: one change, run tests, repeat
  • Use the Boy Scout Rule: leave code better than you found it
  • Refactoring is ongoing, not a separate phase—build it into daily work
Common Pitfalls to Avoid
  • Refactoring and adding features at the same time (do one or the other)
  • Big-bang refactoring instead of small steps
  • Refactoring without tests (too risky)
  • Asking permission to refactor (it's part of the job)

Practice Exercises