DRY, KISS, YAGNI в контексте паттернов

DRY (Don't Repeat Yourself), KISS (Keep It Simple, Stupid), YAGNI (You Aren't Gonna Need It) — три фундаментальных принципа разработки, определяющие, когда применять паттерны, а когда они вредны.

Зачем нужно

Паттерны проектирования — инструменты, а не правила. Применение Abstract Factory ради одной реализации или Observer ради одного подписчика — нарушение YAGNI и KISS. DRY предупреждает от дублирования, но его избыточное применение ведёт к «Wrong Abstraction» — преждевременной абстракции, которую потом сложно изменить. Эти принципы помогают принять решение: нужен ли паттерн сейчас.

Где используется

  • Код-ревью: аргументация решений по рефакторингу
  • Выбор момента введения паттерна: «правило трёх» для DRY
  • Оценка сложности: KISS как критерий приёмки кода
  • Технический долг: YAGNI предотвращает over-engineering
  • Архитектурные решения: нужна ли абстракция прямо сейчас

Основной контент

DRY — Don't Repeat Yourself

// НАРУШЕНИЕ DRY: дублирование логики валидации
function createUser(name, email) {
  if (!name || name.length < 2) throw new Error('Некорректное имя');
  if (!email || !email.includes('@')) throw new Error('Некорректный email');
  return { name, email };
}

function updateUser(id, name, email) {
  if (!name || name.length < 2) throw new Error('Некорректное имя'); // дубль!
  if (!email || !email.includes('@')) throw new Error('Некорректный email'); // дубль!
  return db.update(id, { name, email });
}

// СОБЛЮДЕНИЕ DRY: выносим валидацию
function validateUserData({ name, email }) {
  if (!name || name.length < 2) throw new Error('Некорректное имя');
  if (!email || !email.includes('@')) throw new Error('Некорректный email');
}

function createUser(data) {
  validateUserData(data);
  return { ...data };
}

function updateUser(id, data) {
  validateUserData(data);
  return db.update(id, data);
}

// Осторожно: «Правило трёх» — абстрагируй только при третьем повторении.
// Два похожих куска кода — ещё не дублирование.

KISS — Keep It Simple, Stupid

// НАРУШЕНИЕ KISS: излишняя сложность
class UserRepositoryFactory {
  static create(config) {
    return new UserRepository(
      new DatabaseAdapter(
        new ConnectionPool(config),
        new QueryBuilder
      )
    );
  }
}

// Если это единственный способ работы с пользователями — явный overkill.
// KISS: начни с простого
async function getUser(id) {
  const row = await db.query('SELECT * FROM users WHERE id = $1', [id]);
  return row[0] || null;
}

// KISS не означает «писать плохой код».
// Это «не добавляй сложность без необходимости».

// Хороший тест на KISS: может ли другой разработчик понять код за 2 минуты?
const getActiveUsers = (users) => users.filter(u => u.isActive);
// vs
const getActiveUsers = (users) =>
  users.reduce((acc, u) => u.isActive ? [...acc, u] : acc, );
// filter понятнее — используй filter

YAGNI — You Aren't Gonna Need It

// НАРУШЕНИЕ YAGNI: реализация «на вырост»
class NotificationService {
  send(type, data) {
    switch (type) {
      case 'email': return this.sendEmail(data);
      case 'sms': return this.sendSMS(data);
      case 'push': return this.sendPush(data);
      case 'slack': return this.sendSlack(data);   // не нужно сейчас
      case 'telegram': return this.sendTelegram(data); // не нужно сейчас
    }
  }
}

// YAGNI: реализуй только то, что нужно сейчас
class NotificationService {
  sendEmail(data) { /* только email */ }
  // Slack добавим, когда появится реальная задача
}

// Когда применять паттерны (проверка YAGNI):
// Abstract Factory: ТОЛЬКО если уже есть 2+ семейства объектов
// Observer: ТОЛЬКО если уже есть 2+ подписчика
// Strategy: ТОЛЬКО если уже есть 2+ алгоритма для подстановки

// Рефакторинг к паттерну лучше, чем преждевременный паттерн:
// Сначала пишем прямолинейный код, потом — при реальной необходимости — вводим паттерн

Баланс принципов

// DRY vs YAGNI: не абстрагируй ради одного использования
// Даже если логика повторяется дважды — подождите третьего раза

// KISS vs DRY: иногда небольшое дублирование проще абстракции
// «Дублирование лучше неправильной абстракции» (Sandi Metz)

// Принципы в действии при рефакторинге:
// 1. Код работает (не применяй YAGNI преждевременно)
// 2. Убрать дублирование (DRY при реальной необходимости)
// 3. Упростить (KISS — минимальная сложность для задачи)
// 4. Ввести паттерн (только если без него код сложнее, чем с ним)

Частые ошибки

  • DRY как самоцель: абстракция двух похожих, но семантически разных кусков кода даёт неудобную «протекающую» абстракцию. Ждите третьего использования.
  • YAGNI как оправдание технического долга: «нам не нужно» не означает «пишем грязный код». YAGNI — о функциях, не о качестве.
  • KISS против сложных задач: KISS не требует примитивного кода. Сложная задача требует сложного решения — KISS требует не добавлять сложность сверх необходимой.

Связанные темы

Ресурсы