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 требует не добавлять сложность сверх необходимой.