Work Methodologies DefinedTerm

Refactoring (Code Refactoring)

Also known as: Refactoring, Code Restructuring

The process of restructuring existing code to improve design and readability without changing external behavior.

Updated: 2026-01-04

Definition

Refactoring (or Code Refactoring) is the practice of restructuring existing code to improve its internal design, readability, and maintainability without changing observable external behavior. Refactoring reorganizes code structure while keeping functionality and output identical.

The term was popularized by Martin Fowler in the book “Refactoring: Improving the Design of Existing Code” (1999), which cataloged specific techniques (Extract Method, Move Field, Replace Conditional with Polymorphism) that can be systematically applied. The book defines refactoring as “a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior”.

Refactoring is fundamental for managing technical debt: it allows repaying debt without risky big-rewrites. It’s also the third step of the Test-Driven Development cycle (Red-Green-Refactor).

Refactoring Goals

Improve Design

Reduce complexity: simplify complex logic, reduce cyclomatic complexity, eliminate deep nested conditionals.

Eliminate duplication: apply DRY (Don’t Repeat Yourself), extract commonality into reusable functions/classes.

Increase cohesion: group related code, separate unrelated concerns (Single Responsibility Principle).

Reduce coupling: minimize dependencies between modules, make components more independent and replaceable.

Improve Readability

Meaningful naming: rename variables/functions/classes with descriptive names (“calculate_total_price” instead of “ctp”).

Extract intent: replace magic numbers with named constants, extract complex expressions into clearly named variables.

Remove dead code: eliminate unused code, commented-out code, expired feature flags.

Facilitate Future Features

Prepare extension: refactoring makes adding features easier. “Make the change easy, then make the easy change” (Kent Beck).

Reduce brittleness: fragile code (where small change breaks many things) is made more robust and modular.

Refactoring Catalog (Fowler)

Extract Method

Extract code portion into separate method with descriptive name.

# Before
def print_owing():
    print_banner()
    # print details
    print(f"name: {self.name}")
    print(f"amount: {self.get_outstanding()}")

# After
def print_owing():
    print_banner()
    print_details()

def print_details():
    print(f"name: {self.name}")
    print(f"amount: {self.get_outstanding()}")

Rename Variable/Method

Change name to reflect intent.

// Before
let d = calculateTime();

// After
let elapsedTimeInDays = calculateTime();

Replace Conditional with Polymorphism

Replace switch/if-else with inheritance and polymorphism.

// Before
if (type == ENGINEER) { return monthlySalary; }
else if (type == SALESMAN) { return monthlySalary + commission; }

// After (with polymorphism)
abstract class Employee { abstract int payAmount(); }
class Engineer extends Employee { int payAmount() { return monthlySalary; } }
class Salesman extends Employee { int payAmount() { return monthlySalary + commission; } }

Extract Class

When a class does too much, split into two classes with separate responsibilities.

Inline Method

Opposite of Extract Method: if method is trivial or used only once, incorporate it into caller.

Complete catalog: Fowler documented 70+ named refactorings in the book and on refactoring.com/catalog.

When to Refactor

Rule of Three (Don Roberts)

First time: write code. Second time: duplicate (with reluctance). Third time: refactor to eliminate duplication.

“Three strikes and you refactor”.

Continuous Refactoring

Opportunistic refactoring: every time you touch code, leave it a bit cleaner. Boy Scout Rule: “leave the code cleaner than you found it”.

Preparatory refactoring: before adding feature, refactor to make addition easy. “Make the change easy (warning: this may be hard), then make the easy change” (Kent Beck).

Comprehension refactoring: while reading code to understand it, refactor to make it clearer. Rename variable, extract method. Refactoring documents your understanding.

Dedicated Refactoring Time

Tech debt sprints: every N feature sprints, dedicate sprint to major refactoring. Controversial because it creates separation between “feature work” and “quality work”.

20% time: allocate 15-20% of sprint velocity to tech debt paydown, including refactoring. More sustainable than dedicated sprints.

Refactoring Safety

Prerequisite: Automated Tests

Refactoring is safe only with comprehensive test suite. Tests verify that behavior hasn’t changed.

Workflow: run tests (green) → refactor → run tests (green). If tests fail after refactor, you introduced regression: undo and retry.

Test-first refactoring: if area has no tests, write tests before refactoring (characterization test). “Working Effectively with Legacy Code” (Feathers) has techniques.

Small Steps

Effective refactoring proceeds with micro-steps: tiny changes, tested frequently (every 2-5 minutes). If you go down rabbit hole, undo is easy.

Anti-pattern: big-bang refactor where you modify 50 files in 2 weeks. High risk, hard to review, inevitable merge conflicts.

Version Control Discipline

Frequent commit: commit after each refactoring step that passes tests. Clear message: “Extract calculateTax method”.

Atomic commit: each commit contains one logical refactoring, not mix of refactor + feature + bugfix. Facilitates review and revert.

Refactoring vs. Rewrite

Refactoring (Incremental)

Modify design preserving behavior, one step at a time. Codebase remains functional during entire process.

Pros: low risk, continuous delivery, gradual improvement. Cons: can be slower for radical transformations.

Rewrite (Big-bang)

Rewrite component/system from scratch. Stop development on old codebase, write new version, then switch.

Pros: opportunity for radical redesign, eliminate all legacy issues. Cons: very high risk, difficult feature parity, “second-system syndrome”, long time to market.

When rewrite: technology stack obsolete beyond salvage (e.g., migration from COBOL), fundamentally broken architecture, completely different business requirements.

Preference: incremental refactoring is almost always preferable. “Strangler Fig Pattern” (gradual replacement) is middle ground.

Tool Support

IDE-Automated Refactoring

Modern IDEs (IntelliJ, VS Code, Eclipse) have automated refactoring:

  • Rename symbol (with scope awareness)
  • Extract method/variable/constant
  • Inline variable/method
  • Move class
  • Change signature

Advantage: IDE guarantees refactoring is behavior-preserving, updates all references.

Static Analysis

Tools like SonarQube, CodeClimate, ReSharper identify code smells and suggest refactoring.

Metrics: cyclomatic complexity, code duplication, method length, class coupling.

Refactoring-Aware VCS

Git doesn’t understand refactoring: “rename file” appears as “delete + create”. Tools like Plastic SCM, GitLens have semantic diff that understand refactoring.

Practical Considerations

Balancing: don’t over-refactor. “Premature optimization is root of all evil” (Knuth). Refactor when pain is real, not anticipatory.

Team agreement: significant refactoring (architectural change) should be discussed with team, not unilateral. Use ADR (Architectural Decision Record).

Refactoring in PR: distinguish refactoring PR from feature PR. Reviewing 500-line refactor + 200-line feature together is cognitive overload. Split.

Legacy code: applying refactoring to codebase without tests requires first writing tests (characterization test). It’s upfront investment but necessary for safety.

Metrics: track code quality metrics (complexity, duplication) over time to verify that refactoring is improving codebase, not worsening.

Common Misconceptions

”Refactoring means rewriting the code”

No. Rewrite is different from refactoring. Rewrite changes implementation wholesale, refactoring modifies structure preserving behavior. Refactoring is incremental, rewrite is big-bang.

”We don’t have time to refactor”

Paradox: not refactoring slows development in long term because code becomes increasingly complex. “We don’t have time to go slow” (Kent Beck). Refactoring is investment that pays future velocity.

”Refactoring is only for legacy code”

False. Refactoring is continuous practice even on new code. Design emerges iteratively, first attempt is rarely optimal. TDD includes refactoring as third step of cycle (Red-Green-Refactor).

”Just use IDE’s automated refactoring tools”

Tools help but aren’t sufficient. Automated refactoring covers mechanical transformation (rename, extract), but design-level refactoring (changing architecture, applying patterns) requires human judgment. Tools are enablers, not substitutes for skill.

Sources