Boumlik BrahimBoumlik Brahim
Back to Journal
Software Design

Refactoring: The Art of Clean Code

A comprehensive guide to refactoring techniques. Learn to identify code smells, reduce technical debt, and apply patterns like Extract Method, Strategy, and Adapter to improve code quality.

Refactoring is the art of improving the internal structure of code without changing its external behavior. It's the key to maintaining a healthy codebase, reducing technical debt, and keeping development velocity high. In this comprehensive guide, we'll explore essential refactoring techniques, identify common code smells, and learn how to apply these patterns using clean TypeScript.

Why Refactoring Matters

Code rot is inevitable. As requirements change and quick fixes accumulate, the design degrades. Refactoring is the process of reversing this decay.

  • Improved Readability: Code is read far more often than it is written. Refactoring makes code self-documenting.
  • Reduced Complexity: Breaking down large functions and classes makes the system easier to understand.
  • Bug Prevention: Clean code hides fewer bugs. Complex conditionals and side effects are breeding grounds for errors.
  • Faster Development: It's faster to add features to a clean codebase than a messy one.

Code Smells: When to Refactor

Code smells are indicators of problems in your code. They aren't bugs, but they suggest weaknesses that may slow down development or increase the risk of bugs or failures in the future.

1. Bloaters

Bloaters are code, methods, and classes that have increased to such gargantuan proportions that they are hard to work with.

  • Long Method: A method contains too many lines of code. Generally, any method longer than 10 lines should make you start asking questions.
  • Large Class: A class contains many fields/methods/lines of code. It usually tries to do too much (God Object).
  • Long Parameter List: More than three or four parameters for a method.

2. Object-Orientation Abusers

All these smells are incomplete or incorrect application of object-oriented programming principles.

  • Switch Statements: You have a complex switch operator or sequence of if statements.
  • Refused Bequest: A subclass uses only some of the methods and properties inherited from its parents.

3. Change Preventers

These smells mean that if you need to change something in one place in your code, you have to make many changes in other places too.

  • Divergent Change: You find yourself having to change the same class in many different ways for different reasons.
  • Shotgun Surgery: Making any modification requires that you make many small changes to many different classes.

Essential Refactoring Techniques

1. Extract Method

Problem: You have a code fragment that can be grouped together.

Solution: Move this code to a separate new method (or function) and replace the old code with a call to the method.


// Before
function printOwning(invoice: Invoice): void {
  printBanner();
  let outstanding = calculateOutstanding(invoice);

  // print details
  console.log("name: " + invoice.customer);
  console.log("amount: " + outstanding);
}

// After
function printOwning(invoice: Invoice): void {
  printBanner();
  const outstanding = calculateOutstanding(invoice);
  printDetails(invoice, outstanding);
}

function printDetails(invoice: Invoice, outstanding: number): void {
  console.log(`name: ${invoice.customer}`);
  console.log(`amount: ${outstanding}`);
}
    

2. Replace Conditional with Polymorphism

Problem: You have a conditional that performs various actions depending on object type or properties.

Solution: Create subclasses matching the branches of the conditional.


// Before
enum BirdType { EUROPEAN, AFRICAN, NORWEGIAN_BLUE }

class Bird {
  constructor(private type: BirdType, private voltage: number, private coconuts: number) {}

  getSpeed(): number {
    switch (this.type) {
      case BirdType.EUROPEAN:
        return this.getBaseSpeed();
      case BirdType.AFRICAN:
        return this.getBaseSpeed() - this.getLoadFactor() * this.coconuts;
      case BirdType.NORWEGIAN_BLUE:
        return (this.voltage > 100) ? 0 : this.getBaseSpeed(this.voltage);
      default:
        throw new Error("Unknown bird type");
    }
  }

  private getBaseSpeed(val?: number): number { return 10; }
  private getLoadFactor(): number { return 1.5; }
}

// After
abstract class Bird {
  abstract getSpeed(): number;
}

class European extends Bird {
  getSpeed(): number {
    return 10;
  }
}

class African extends Bird {
  constructor(private coconuts: number) { super(); }
  
  getSpeed(): number {
    return 10 - 1.5 * this.coconuts;
  }
}

class NorwegianBlue extends Bird {
  constructor(private voltage: number) { super(); }

  getSpeed(): number {
    return (this.voltage > 100) ? 0 : 10 + this.voltage;
  }
}
    

3. Introduce Parameter Object

Problem: Your methods contain a repeating group of parameters.

Solution: Replace these parameters with an interface or type alias.


// Before
function amountInvoiced(start: Date, end: Date): number { /*...*/ }
function amountReceived(start: Date, end: Date): number { /*...*/ }
function amountOverdue(start: Date, end: Date): number { /*...*/ }

// After
interface DateRange {
  start: Date;
  end: Date;
}

function amountInvoiced(range: DateRange): number { /*...*/ }
function amountReceived(range: DateRange): number { /*...*/ }
function amountOverdue(range: DateRange): number { /*...*/ }
    

4. Rename Method

Problem: The name of a method does not explain what the method does.

Solution: Rename the method.

Ideally, code should read like English. If you need comments to explain what a function does, try renaming the function first.

Refactoring in Practice

In our modern ecosystem, these principles apply directly to our tools:

  • React Components: Extract Method becomes "Extract Component".
  • Custom Hooks: Complex logic in components is extracted into reusable hooks.
  • NestJS Services: Logic in Controllers is extracted into Services as methods.

Conclusion

Refactoring is not a one-time event; it's a habit. It should be part of your daily workflow. The Boy Scout Rule applies perfectly here: "Always leave the code better than you found it."

Boumlik BrahimBoumlik Brahim

Technical Lead & IT Architecture Expert. Architecting resilient cloud ecosystems and scalable, high-performance solutions.

boumlik01brahim@gmail.com

Explore

Connect

  • Boumlik-Brahim
  • brahim-boumlik
  • @BOUMLIKBRAHIM4
  • @brahimboumlik
  • Brahim Boumlik

Status

Location

Casablanca, Morocco

Download Resume

© 2026 Boumlik Brahim.

GitHubLinkedInTwitterInstagramFacebook