← Назад к вопросам

Расшифруй аббревиатуру YAGNI

1.2 Junior🔥 151 комментариев
#Python Core#Soft Skills#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

YAGNI: You Aren't Gonna Need It

YAGNI (Вам это не понадобится) — это принцип разработки, который говорит: **не добавляй функционал, пока он реально не нужен**. Это одна из самых важных идей чистого кода.

Суть принципа

YAGNI = не предугадывай — решай текущие проблемы

# Вопрос для себя:
# "Нужна ли мне эта функция СЕЙЧАС?"
# - Если ДА → добавляй
# - Если НЕТ (может быть нужна в будущем) → не добавляй!

Пример YAGNI нарушения

# ❌ ПЛОХО: Переинженерство
# Пишешь общий фреймворк "на будущее"

class BaseDataProcessor:
    """Универсальный процессор данных"""
    
    def __init__(self):
        self.filters = []
        self.transformers = []
        self.validators = []
        self.handlers = []
        self.cache = {}
        self.metrics = {}
        self.logger = None
        self.config = {}
    
    def add_filter(self, filter_func):
        self.filters.append(filter_func)
    
    def add_transformer(self, transformer_func):
        self.transformers.append(transformer_func)
    
    def process(self, data):
        # Очень сложная логика с поддержкой всего на свете
        pass

# Но на самом деле нужно было только:
class SimpleDataProcessor:
    def process(self, data):
        return [item * 2 for item in data]

# ❌ 500 строк кода
# ✅ 1 строка кода

Правильное применение YAGNI

# ✅ ХОРОШО: Решаешь текущую задачу

# Требование: "Обработать CSV файл с продуктами"

class ProductLoader:
    """Загружает продукты из CSV"""
    
    def load(self, filename):
        import csv
        products = []
        with open(filename) as f:
            reader = csv.DictReader(f)
            for row in reader:
                products.append({
                    'id': row['id'],
                    'name': row['name'],
                    'price': float(row['price'])
                })
        return products

# Простая функция, которая решает проблему
# Если потом понадобится JSON, Excel, БД → добавишь

Сценарий: Расширение функционала

# Этап 1: CSV работает
class ProductLoader:
    def load(self, filename):
        import csv
        # CSV логика

# Месяц спустя...
# Требование: "Нужна загрузка из JSON тоже"

# ✅ Правильный путь: рефакторим, добавляем
from abc import ABC, abstractmethod

class DataLoader(ABC):
    @abstractmethod
    def load(self, filename):
        pass

class CSVProductLoader(DataLoader):
    def load(self, filename):
        import csv
        # CSV логика

class JSONProductLoader(DataLoader):
    def load(self, filename):
        import json
        # JSON логика

class ExcelProductLoader(DataLoader):
    def load(self, filename):
        import openpyxl
        # Excel логика

class ProductService:
    def __init__(self, loader: DataLoader):
        self.loader = loader
    
    def load_products(self, filename):
        return self.loader.load(filename)

# Использование
csv_service = ProductService(CSVProductLoader())
json_service = ProductService(JSONProductLoader())
excel_service = ProductService(ExcelProductLoader())

# ❌ НЕ ДОБАВЛЯЙ СРАЗУ. Только когда нужно!

Типичные нарушения YAGNI

1. Универсальные параметры

# ❌ YAGNI нарушение
def send_notification(message, medium=None, retry_count=None, 
                      retry_delay=None, priority=None, 
                      timeout=None, callback=None, metadata=None):
    """Отправить уведомление (со всеми возможными опциями)"""
    # 100 строк кода для обработки всех параметров

# ✅ YAGNI: делай просто
def send_notification(message):
    # Отправить по Email (текущее требование)
    send_email(message)

# Если потом нужно будет SMS, retry и т.д. → тогда добавишь

2. Предусмотрительные абстракции

# ❌ YAGNI нарушение
class Logger:
    def __init__(self):
        self.handlers = []
        self.filters = []
        self.formatters = {}
    
    def add_handler(self, handler):
        self.handlers.append(handler)
    
    def add_filter(self, filter_fn):
        self.filters.append(filter_fn)
    
    def set_formatter(self, name, formatter):
        self.formatters[name] = formatter
    
    # ... ещё 50 методов ...

# ✅ YAGNI: используй встроенное
import logging

logger = logging.getLogger(__name__)
logger.info("Something happened")

3. Готовизация к масштабированию

# ❌ YAGNI нарушение
class UserRepository:
    def __init__(self):
        self.cache = {}
        self.read_replicas = []
        self.write_replicas = []
        self.circuit_breaker = CircuitBreaker()
        self.metrics = MetricsCollector()
    
    def get_user(self, user_id):
        # Проверяет кеш
        # Проверяет circuit breaker
        # Логирует метрики
        # Балансирует нагрузку
        # Кеширует результат
        # Отправляет события
        # ... и ещё 20 операций ...

# ❌ ОДИН пользователь на API → ВСЁ ЭТО ЗАЧЕМ?

# ✅ YAGNI: начни с простого
def get_user(user_id):
    user = User.objects.get(id=user_id)
    return user

# Когда 10000 пользователей → ТОГДА добавляй кеш, реплики и т.д.

YAGNI vs. Подготовка

Когда МОЖНО "подготовиться":

# ✅ Если это стандарт или best practice
from typing import Optional, List

def process_users(users: List[dict]) -> List[dict]:
    """Типизация — стандарт, используй всегда"""
    return users

# ✅ Если это требует минимальных усилий
class User:
    def __init__(self, name, email):
        self.name = name
        self.email = email
    
    def __repr__(self):
        return f"User(name={self.name})"
    # __repr__ — это правило, не переинженерство

# ✅ Если это требование безопасности
def get_user(user_id):
    if not isinstance(user_id, int):
        raise ValueError("user_id must be int")
    # Валидация — всегда нужна

Когда НЕЛЬЗЯ "подготавливаться":

# ❌ Целые новые модули на будущее
# ❌ Конфигурационные системы, которые может никто не использовать
# ❌ Абстрактные интерфейсы без реальной необходимости
# ❌ Многоуровневый кеширование на ранних стадиях

Цикл разработки с YAGNI

# Этап 1: Делай минимум
def calculate_total(items):
    return sum(item['price'] * item['quantity'] for item in items)

# Месяц спустя...
# Требование: "Нужны скидки на основе суммы"

# Этап 2: Расширяй ПОТОМ
def calculate_total(items, discount_percent=0):
    total = sum(item['price'] * item['quantity'] for item in items)
    return total * (1 - discount_percent / 100)

# Ещё месяц...
# Требование: "Нужны разные скидки для разных категорий"

# Этап 3: Рефакторишь
class PricingEngine:
    def __init__(self, discount_rules):
        self.discount_rules = discount_rules
    
    def calculate_total(self, items):
        # Логика с разными скидками
        pass

# Путь развития:
# Простая функция → параметры → объект → паттерны
# НЕ наоборот!

Как распознать YAGNI нарушение

Красные флаги:

# 🚩 "Нам потом это точно понадобится"
class SomeClass:
    # Куча методов для гипотетических сценариев
    def future_feature_1(self): pass
    def future_feature_2(self): pass
    def future_feature_3(self): pass

# 🚩 "На всякий случай добавлю параметр"
def process(data, optional_param_1=None, optional_param_2=None):
    # Но никто это не использует
    pass

# 🚩 "Сделаю это максимально гибким"
class ConfigurationEngine:
    # 300 строк кода конфигурации для одного простого случая

# 🚩 "Добавлю слой абстракции"
# Но он нужен только для одной конкретной реализации

class DataHandler(ABC):  # ❌ не нужна
    @abstractmethod
    def handle(self, data): pass

class CSVDataHandler(DataHandler):  # ❌ только один класс
    def handle(self, data):
        # CSV логика

YAGNI в контексте SOLID

YAGNI — это упрощение без нарушения SOLID:

# ✅ Простой класс (YAGNI) + SOLID
class EmailService:
    def send(self, to: str, subject: str, body: str):
        # Одна ответственность (S)
        # Легко раскрыть для расширения (O)
        # Может быть переопределён (L)
        pass

# ❌ Сложный класс (нарушение YAGNI) + SOLID
class NotificationService:
    def send_email(self, to, subject, body): pass
    def send_sms(self, phone, text): pass
    def send_push(self, device_id, message): pass
    def send_webhook(self, url, payload): pass
    def send_telegram(self, chat_id, message): pass
    def send_slack(self, channel, message): pass
    def send_discord(self, webhook_url, content): pass
    def send_whatsapp(self, phone, message): pass
    # Слишком много ответственности (S нарушена)

Баланс: YAGNI vs. SOLID vs. DRY

# ✅ ХОРОШО: Баланс
class UserRepository:
    def get(self, user_id):
        return User.objects.get(id=user_id)
    
    def filter_by_email(self, email):
        return User.objects.filter(email=email)
    
    def save(self, user):
        user.save()
        return user

# Простой, не переинженерный, но полезный
# YAGNI: нет лишних фич
# SOLID: одна ответственность
# DRY: не дублируем запросы

# ❌ ПЛОХО: Нарушение YAGNI + SOLID
class BaseRepository(ABC):
    @abstractmethod
    def get(self, id): pass
    
    @abstractmethod
    def filter(self, **kwargs): pass
    
    @abstractmethod
    def save(self, obj): pass
    
    # ... 20 других методов ...

class UserRepository(BaseRepository):
    # Реализует методы, большинство из которых не используются

Вывод

YAGNI заповеди:

  1. Пиши код для текущих требований, не для будущих
  2. Начинай просто, рефакторишь потом
  3. Если сомневаешься — не добавляй
  4. Каждая строка кода — это долг (её нужно поддерживать)
  5. Гибкость имеет цену (сложность кода)

Золотое правило: Лучше иметь 100 строк кода, который решает проблему, чем 1000 строк универсального фреймворка, который может что-то делать когда-нибудь в будущем.