Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
YAGNI — You Aren't Gonna Need It
YAGNI — это принцип в разработке, который говорит: не добавляй функционал, пока он не будет реально нужен. Это один из столпов простоты и эффективности в коде.
Суть принципа
YAGNI утверждает: Не пиши код для функций, которые ты думаешь, что понадобятся в будущем. Результат часто:
- Код никогда не используется
- Усложняется текущая система
- Тратится время зря
- Увеличивается техдолг
Отсюда мантра: 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:
- Есть ли требование? → Если НЕТ, не пиши код
- Есть ли тест для этого? → Если НЕТ, требования не утверждены
- Используется ли в коде? → Если НЕТ, это trash
- Просила ли задача? → Если НЕТ, это спекуляция
Баланс: когда ВСЕ ЖЕ нужна гибкость
Иногда расширяемость реально нужна:
# Хорошо: используй 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 в практике: рекомендации
- Пиши самый простой код, который работает
- Добавляй сложность ТОЛЬКО когда она нужна
- Один цикл разработки = одна фича
- Абстракции растут из повторения кода, а не из предположений
- Если сомневаешься — выбирай простой вариант
Антипаттерн: золотой молоток
# Разработчик купил сложный фреймворк и использует везде
from some_heavy_framework import Framework, Plugin, Decorator, Handler
# Для простого скрипта обработки данных!
# Это YAGNI + золотой молоток
# Правильно:
data = parse_csv(filename)
for row in data:
process_row(row)
Итог
YAGNI учит нас писать код для текущих требований, а не для гипотетического будущего. Результат:
- Меньше кода → легче понять и поддерживать
- Быстрее разработка → не тратим время на лишнее
- Проще рефакторинг → когда появится новое требование
- Меньше багов → меньше кода = меньше ошибок
Но YAGNI не противоречит хорошей архитектуре — это просто означает: не усложняй без причины, а когда сложность нужна, добавляй её обоснованно.