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

Что такое YAGNI?

1.6 Junior🔥 141 комментариев
#Soft Skills#Архитектура и паттерны

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

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

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

YAGNI — You Aren't Gonna Need It

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

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

YAGNI утверждает: Не пиши код для функций, которые ты думаешь, что понадобятся в будущем. Результат часто:

  1. Код никогда не используется
  2. Усложняется текущая система
  3. Тратится время зря
  4. Увеличивается техдолг

Отсюда мантра: Write code for today's requirements, not tomorrow's speculations.

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

Пример 1: Оверпараметризация

# ПЛОХО — YAGNI violation
class UserRepository:
    def find_by_email(
        self,
        email: str,
        include_deleted: bool = False,
        include_suspended: bool = False,
        include_inactive: bool = False,
        use_cache: bool = True,
        cache_ttl: int = 3600,
        retry_count: int = 3,
        timeout_seconds: float = 10,
        return_dto: bool = False,
        log_access: bool = True
    ) -> User:
        # 10 параметров!
        # Половину из них, вероятно, никогда не используем
        pass

# ХОРОШО — реальная необходимость
class UserRepository:
    def find_by_email(self, email: str) -> User | None:
        # Всё просто, только нужное
        pass

Если позже потребуется кэширование — добавим. Когда потребуется логирование — добавим.

Пример 2: Абстракции для одного использования

# ПЛОХО — YAGNI violation
class DataProcessor:
    def process(self, data: RawData) -> ProcessedData:
        # Предполагаем, что будут разные форматы (CSV, JSON, XML)
        # Создаём абстрактный класс ProcessorFactory
        pass

class ProcessorFactory:
    @staticmethod
    def get_processor(format: str) -> BaseProcessor:
        if format == "csv":
            return CSVProcessor()
        elif format == "json":
            return JSONProcessor()
        # ...

class BaseProcessor(ABC):
    @abstractmethod
    def process(self) -> Any:
        pass

class CSVProcessor(BaseProcessor):
    def process(self) -> Any:
        pass

class JSONProcessor(BaseProcessor):
    def process(self) -> Any:
        pass

# Но используется только CSV!

# ХОРОШО — простое решение сейчас
class DataProcessor:
    def process_csv(self, data: str) -> ProcessedData:
        # Читаем CSV
        reader = csv.reader(io.StringIO(data))
        # ...
        return ProcessedData(rows)

# Если потом нужен JSON — добавим
def process_json(self, data: str) -> ProcessedData:
    obj = json.loads(data)
    # ...
    return ProcessedData(rows)

Пример 3: Избыточная производительность

# ПЛОХО — YAGNI violation
class CacheManager:
    def __init__(self):
        # Сложный кэш с TTL, LRU, распределённой синхронизацией
        self.redis_client = redis.Redis()
        self.local_cache = LRUCache(maxsize=10000)
        self.background_updater = BackgroundThread()
        self.stats_collector = StatsCollector()
        # Всё это может не быть нужным
        pass

# ХОРОШО — начни просто
class CacheManager:
    def __init__(self):
        self.cache = {}  # Простой dict
    
    def get(self, key: str) -> Any:
        return self.cache.get(key)
    
    def set(self, key: str, value: Any) -> None:
        self.cache[key] = value

# Позже, если нужна Redis:
class RedisCacheManager(CacheManager):
    def __init__(self):
        self.redis = redis.Redis()
    
    def get(self, key: str) -> Any:
        return self.redis.get(key)
    
    def set(self, key: str, value: Any) -> None:
        self.redis.set(key, value)

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

Шаг 1: Пиши для текущих требований

# Требование: пользователь может зарегистрироваться с email и паролем

class RegisterUserUseCase:
    def __init__(self, repo: UserRepository):
        self.repo = repo
    
    def execute(self, email: str, password: str) -> User:
        if self.repo.find_by_email(email):
            raise UserAlreadyExistsError()
        
        user = User(
            email=email,
            password_hash=hash_password(password)
        )
        self.repo.save(user)
        return user

Шаг 2: Рефакторь, когда появится новое требование

# Новое требование: регистрация через Google OAuth
# Теперь нужна общая абстракция

class AuthProvider(ABC):
    @abstractmethod
    def authenticate(self, credentials: Any) -> User:
        pass

class EmailPasswordAuthProvider(AuthProvider):
    def authenticate(self, email: str, password: str) -> User:
        # Логика email/password
        pass

class GoogleOAuthProvider(AuthProvider):
    def authenticate(self, google_token: str) -> User:
        # Логика Google OAuth
        pass

# Теперь абстракция ОПРАВДАНА — она решает реальную проблему

YAGNI vs SOLID

Полезная таблица:

ПринципЧто делаетКогда применить
YAGNIНе добавляй лишнееСЕЙЧАС — при написании кода
SOLIDСтруктурируй хорошоПОТОМ — при рефакторинге
DRYНе повторяйПри обнаружении дублирования
KISSПростое > сложноеВсегда выбирай простой вариант
# Пример: когда твой код нарушает YAGNI

class UserService:
    def __init__(self):
        # Готовимся к несуществующим требованиям
        self.logger = configure_advanced_logger()  # Может не понадобиться
        self.metrics_collector = MetricsCollector()  # Никто не просил
        self.background_job_queue = RabbitMQ()  # Нет требования
        self.cache = Redis()  # Может быть, когда-нибудь...
        self.circuit_breaker = CircuitBreaker()  # Может быть...

# Это типичный случай YAGNI violation

Как узнать, нужен ли функционал?

Тест YAGNI:

  1. Есть ли требование? → Если НЕТ, не пиши код
  2. Есть ли тест для этого? → Если НЕТ, требования не утверждены
  3. Используется ли в коде? → Если НЕТ, это trash
  4. Просила ли задача? → Если НЕТ, это спекуляция

Баланс: когда ВСЕ ЖЕ нужна гибкость

Иногда расширяемость реально нужна:

# Хорошо: используй Strategy Pattern когда есть требование расширяемости
from abc import ABC, abstractmethod

class PaymentProcessor(ABC):
    @abstractmethod
    def process(self, amount: float) -> PaymentResult:
        pass

class StripeProcessor(PaymentProcessor):
    def process(self, amount: float) -> PaymentResult:
        # Stripe API
        pass

class PayPalProcessor(PaymentProcessor):
    def process(self, amount: float) -> PaymentResult:
        # PayPal API
        pass

# Это ОПРАВДАНО, если реально есть требование поддерживать
# разные платёжные системы — это не YAGNI, это реальная задача

YAGNI в практике: рекомендации

  1. Пиши самый простой код, который работает
  2. Добавляй сложность ТОЛЬКО когда она нужна
  3. Один цикл разработки = одна фича
  4. Абстракции растут из повторения кода, а не из предположений
  5. Если сомневаешься — выбирай простой вариант

Антипаттерн: золотой молоток

# Разработчик купил сложный фреймворк и использует везде

from some_heavy_framework import Framework, Plugin, Decorator, Handler

# Для простого скрипта обработки данных!
# Это YAGNI + золотой молоток

# Правильно:
data = parse_csv(filename)
for row in data:
    process_row(row)

Итог

YAGNI учит нас писать код для текущих требований, а не для гипотетического будущего. Результат:

  • Меньше кода → легче понять и поддерживать
  • Быстрее разработка → не тратим время на лишнее
  • Проще рефакторинг → когда появится новое требование
  • Меньше багов → меньше кода = меньше ошибок

Но YAGNI не противоречит хорошей архитектуре — это просто означает: не усложняй без причины, а когда сложность нужна, добавляй её обоснованно.