Ответ
Практически никогда в чистом виде. Работа над коммерческими проектами — это постоянный поиск баланса между качеством архитектуры, сроками, бюджетом и меняющимися требованиями бизнеса.
С какими компромиссами сталкиваюсь чаще всего:
- Наследие (Legacy Code): Приходится интегрировать новые фичи в существующую, далекую от идеала, кодобазу. Например, добавлять GraphQL эндпоинт поверх старого монолита с "жирными" моделями, где бизнес-логика размазана по контроллерам.
- Срочные фиксы и хотфиксы: В критической ситуации (продакшен-инцидент) приоритет — скорость восстановления работы, а не красота решения. Позже такой код обязательно выносится в технический долг для рефакторинга.
- Недостаточное понимание предметной области на старте: Часто требования уточняются в процессе. Архитектура, идеальная на бумаге в начале спринта, может оказаться неоптимальной к его концу.
Мой подход: стремлюсь не к "идеальной" архитектуре, а к прагматичной и поддерживаемой.
- Я применяю принципы SOLID и DRY там, где это дает явную выгоду для тестируемости и расширяемости.
- Явно выделяю границы контекста (Bounded Context) даже в монолите, чтобы позже было проще выделить микросервис.
- Пишу код так, чтобы его мог понять и развивать другой разработчик через полгода.
Пример компромисса: вместо идеального Event-Driven подхода с брокером сообщений (Kafka/RabbitMQ) для небольшой фичи можно временно использовать синхронный вызов сервиса, но обернуть его в интерфейс. Это позволит в будущем заменить реализацию на асинхронную, не меняя код вызывающей стороны.