A comprehensive reference of code smells based on Martin Fowler's Refactoring (2nd Edition). Code smells are symptoms of deeper problems—they indicate that something might be wrong with your code's design.

"A code smell is a surface indication that usually corresponds to a deeper problem in the system." — Martin Fowler


Bloaters

Code smells representing something that has grown too large to be handled effectively.

Long Method

Signs:

Why it's bad:

Refactorings:

Example (Before):

function processOrder(order) {
  // Validate order (20 lines)
  if (!order.items) throw new Error('No items');
  if (order.items.length === 0) throw new Error('Empty order');
  // ... more validation

  // Calculate totals (30 lines)
  let subtotal = 0;
  for (const item of order.items) {
    subtotal += item.price * item.quantity;
  }
  // ... tax, shipping, discounts

  // Send notifications (20 lines)
  // ... email logic
}

Example (After):

function processOrder(order) {
  validateOrder(order);
  const totals = calculateOrderTotals(order);
  sendOrderNotifications(order, totals);
  return { order, totals };
}

Large Class

Signs:

Why it's bad:

Refactorings:

Detection:

Lines of code > 300
Number of methods > 15
Number of fields > 10

Primitive Obsession

Signs:

Why it's bad:

Refactorings:

Example (Before):

const user = {
  email: 'john@example.com',     // Just a string
  phone: '1234567890',           // Just a string
  status: 'active',              // Magic string
  balance: 10050                 // Cents as integer
};

Example (After):

const user = {
  email: new Email('john@example.com'),
  phone: new PhoneNumber('1234567890'),
  status: UserStatus.ACTIVE,
  balance: Money.cents(10050)
};

Long Parameter List

Signs:

Why it's bad:

Refactorings:

Example (Before):

function createUser(firstName, lastName, email, phone,
                    street, city, state, zip,
                    isAdmin, isActive, createdBy) {
  // ...
}

Example (After):

function createUser(personalInfo, address, options) {
  // personalInfo: { firstName, lastName, email, phone }
  // address: { street, city, state, zip }
  // options: { isAdmin, isActive, createdBy }
}

Data Clumps

Signs:

Why it's bad:

Refactorings:

Example:

// Data clump: (x, y, z) coordinates
function movePoint(x, y, z, dx, dy, dz) { }
function scalePoint(x, y, z, factor) { }
function distanceBetween(x1, y1, z1, x2, y2, z2) { }

// Extract Point3D class
class Point3D {
  constructor(x, y, z) { }
  move(delta) { }
  scale(factor) { }
  distanceTo(other) { }
}

Object-Orientation Abusers

Smells indicating incomplete or incorrect use of OOP principles.

Switch Statements

Signs:

Why it's bad:

Refactorings:

Example (Before):

function calculatePay(employee) {
  switch (employee.type) {
    case 'hourly':
      return employee.hours * employee.rate;
    case 'salaried':
      return employee.salary / 12;
    case 'commissioned':
      return employee.sales * employee.commission;
  }
}

Example (After):

class HourlyEmployee {
  calculatePay() {
    return this.hours * this.rate;
  }
}

class SalariedEmployee {
  calculatePay() {
    return this.salary / 12;
  }
}

Temporary Field

Signs:

Why it's bad:

Refactorings:


Refused Bequest

Signs:

Why it's bad:

Refactorings:


Alternative Classes with Different Interfaces

Signs:

Why it's bad:

Refactorings:


Change Preventers

Smells that make changes difficult—changing one thing requires changing many others.

Divergent Change

Signs:

Why it's bad:

Refactorings:

Example: A User class changes for:

→ Extract: AuthService, ProfileService, BillingService, NotificationService


Shotgun Surgery

Signs:

Why it's bad:

Refactorings:

Detection: Look for: adding one field requires changes in >5 files.


Parallel Inheritance Hierarchies

Signs:

Why it's bad:

Refactorings:


Dispensables

Something unnecessary that should be removed.

Comments (Excessive)

Signs:

Why it's bad:

Refactorings:

Good vs Bad Comments:

// BAD: Explaining what
// Loop through users and check if active
for (const user of users) {
  if (user.status === 'active') { }
}

// GOOD: Explaining why
// Active users only - inactive are handled by cleanup job
const activeUsers = users.filter(u => u.isActive);

Duplicate Code

Signs:

Why it's bad:

Refactorings:

Detection Rule: Any code duplicated 3+ times should be extracted.


Lazy Class

Signs:

Why it's bad:

Refactorings:


Dead Code

Signs:

Why it's bad:

Refactorings:

Detection:

# Look for unused exports
# Look for unreferenced functions
# IDE "unused" warnings

Speculative Generality

Signs:

Why it's bad:

Refactorings:


Couplers

Smells that represent excessive coupling between classes.

Feature Envy

Signs:

Why it's bad:

Refactorings:

Example (Before):

class Order {
  getDiscountedPrice(customer) {
    // Uses customer data heavily
    if (customer.loyaltyYears > 5) {
      return this.price * customer.discountRate;
    }
    return this.price;
  }
}

Example (After):

class Customer {
  getDiscountedPriceFor(price) {
    if (this.loyaltyYears > 5) {
      return price * this.discountRate;
    }
    return price;
  }
}

Inappropriate Intimacy

Signs:

Why it's bad:

Refactorings:


Message Chains

Signs:

Why it's bad:

Refactorings:

Example:

// Bad: Message chain
const managerName = employee.getDepartment().getManager().getName();

// Better: Hide delegation
const managerName = employee.getManagerName();

Middle Man

Signs:

Why it's bad:

Refactorings:


Smell Severity Guide

Severity Description Action
Critical Blocks development, causes bugs Fix immediately
High Significant maintenance burden Fix in current sprint
Medium Noticeable but manageable Plan for near future
Low Minor inconvenience Fix opportunistically

Quick Detection Checklist

Use this checklist when scanning code:


Further Reading