Комплексний довідник запахів коду на основі книги Мартіна Фаулера Refactoring (2-ге видання). Запахи коду — це симптоми глибших проблем; вони вказують на те, що щось може бути не так з дизайном вашого коду.

«Запах коду — це поверхнева ознака, яка зазвичай відповідає глибшій проблемі в системі.» — Мартін Фаулер


Роздуті елементи (Bloaters)

Запахи коду, що представляють щось, що розрослося занадто великим для ефективної роботи.

Long Method (Довгий метод)

Ознаки:

Чому це погано:

Рефакторинги:

Приклад (до):

function processOrder(order) {
  // Валідація замовлення (20 рядків)
  if (!order.items) throw new Error('No items');
  if (order.items.length === 0) throw new Error('Empty order');
  // ... ще валідація

  // Обчислення підсумків (30 рядків)
  let subtotal = 0;
  for (const item of order.items) {
    subtotal += item.price * item.quantity;
  }
  // ... податок, доставка, знижки

  // Надсилання сповіщень (20 рядків)
  // ... логіка email
}

Приклад (після):

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

Large Class (Великий клас)

Ознаки:

Чому це погано:

Рефакторинги:

Виявлення:

Рядків коду > 300
Кількість методів > 15
Кількість полів > 10

Primitive Obsession (Одержимість примітивами)

Ознаки:

Чому це погано:

Рефакторинги:

Приклад (до):

const user = {
  email: 'john@example.com',     // Просто рядок
  phone: '1234567890',           // Просто рядок
  status: 'active',              // Магічний рядок
  balance: 10050                 // Копійки як ціле число
};

Приклад (після):

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

Long Parameter List (Довгий список параметрів)

Ознаки:

Чому це погано:

Рефакторинги:

Приклад (до):

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

Приклад (після):

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

Data Clumps (Групи даних)

Ознаки:

Чому це погано:

Рефакторинги:

Приклад:

// Група даних: координати (x, y, z)
function movePoint(x, y, z, dx, dy, dz) { }
function scalePoint(x, y, z, factor) { }
function distanceBetween(x1, y1, z1, x2, y2, z2) { }

// Витяг класу Point3D
class Point3D {
  constructor(x, y, z) { }
  move(delta) { }
  scale(factor) { }
  distanceTo(other) { }
}

Зловживання обʼєктною орієнтацією (OO Abusers)

Запахи, що вказують на неповне або некоректне використання принципів ООП.

Switch Statements (Оператори Switch)

Ознаки:

Чому це погано:

Рефакторинги:

Приклад (до):

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;
  }
}

Приклад (після):

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

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

Temporary Field (Тимчасове поле)

Ознаки:

Чому це погано:

Рефакторинги:


Refused Bequest (Відхилена спадщина)

Ознаки:

Чому це погано:

Рефакторинги:


Alternative Classes with Different Interfaces (Альтернативні класи з різними інтерфейсами)

Ознаки:

Чому це погано:

Рефакторинги:


Перешкоди для змін (Change Preventers)

Запахи, що ускладнюють зміни — зміна однієї речі вимагає зміни багатьох інших.

Divergent Change (Дивергентна зміна)

Ознаки:

Чому це погано:

Рефакторинги:

Приклад: Клас User змінюється через:

→ Витягти: AuthService, ProfileService, BillingService, NotificationService


Shotgun Surgery (Хірургія дробовиком)

Ознаки:

Чому це погано:

Рефакторинги:

Виявлення: Шукайте: додавання одного поля вимагає змін у >5 файлах.


Parallel Inheritance Hierarchies (Паралельні ієрархії успадкування)

Ознаки:

Чому це погано:

Рефакторинги:


Непотрібне (Dispensables)

Щось непотрібне, що слід видалити.

Comments (Надмірні коментарі)

Ознаки:

Чому це погано:

Рефакторинги:

Хороші vs погані коментарі:

// ПОГАНО: Пояснює що
// Цикл по користувачах і перевірка чи активні
for (const user of users) {
  if (user.status === 'active') { }
}

// ДОБРЕ: Пояснює чому
// Лише активні — неактивні обробляються задачею очищення
const activeUsers = users.filter(u => u.isActive);

Duplicate Code (Дубльований код)

Ознаки:

Чому це погано:

Рефакторинги:

Правило виявлення: Будь-який код, дубльований 3+ разів, слід витягти.


Lazy Class (Ледачий клас)

Ознаки:

Чому це погано:

Рефакторинги:


Dead Code (Мертвий код)

Ознаки:

Чому це погано:

Рефакторинги:

Виявлення:

# Шукати невикористані експорти
# Шукати невикористані функції
# Попередження IDE "unused"

Speculative Generality (Спекулятивна загальність)

Ознаки:

Чому це погано:

Рефакторинги:


Звʼязувачі (Couplers)

Запахи, що представляють надмірний звʼязок між класами.

Feature Envy (Заздрість до функцій)

Ознаки:

Чому це погано:

Рефакторинги:

Приклад (до):

class Order {
  getDiscountedPrice(customer) {
    // Інтенсивно використовує дані customer
    if (customer.loyaltyYears > 5) {
      return this.price * customer.discountRate;
    }
    return this.price;
  }
}

Приклад (після):

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

Inappropriate Intimacy (Недоречна інтимність)

Ознаки:

Чому це погано:

Рефакторинги:


Message Chains (Ланцюжки повідомлень)

Ознаки:

Чому це погано:

Рефакторинги:

Приклад:

// Погано: Ланцюжок повідомлень
const managerName = employee.getDepartment().getManager().getName();

// Краще: Приховати делегування
const managerName = employee.getManagerName();

Middle Man (Посередник)

Ознаки:

Чому це погано:

Рефакторинги:


Посібник серйозності запахів

Серйозність Опис Дія
Критичний Блокує розробку, спричиняє помилки Виправити негайно
Високий Значний тягар обслуговування Виправити в поточному спринті
Середній Помітний, але керований Запланувати на найближче майбутнє
Низький Незначна незручність Виправляти при нагоді

Контрольний список швидкого виявлення

Використовуйте цей список при скануванні коду:


Додаткове читання