> Когда переходить на микросервисы (Python)
Уровень: senior · Роль: backend · Язык: Python · Категория: Технические вопросы
Компании: Notamedia, МОСГАЗ
Стек: Python
> Пример ответа
Короткий ответ
Переходить на микросервисы стоит, когда монолит начинает мешать развитию: команда выросла и не может эффективно работать над одним кодом, появляются проблемы с масштабированием отдельных частей системы, или требуется независимый деплой и изоляция отказов. При этом у вас уже есть чёткие границы доменов и ресурсы на поддержку инфраструктуры. Если этого нет - микросервисы добавят сложности, а не пользы.
Подробное объяснение
Микросервисы - это не цель, а инструмент. Переход оправдан, когда выполняются несколько условий одновременно:
Организационные причины:
- Размер команды: 10+ разработчиков, работающих над одним монолитом, начинают конфликтовать при merge и деплое
- Разные команды отвечают за разные бизнес-домены, и им нужна автономия в выборе технологий и темпе релизов
- Частота релизов: если вы выпускаете релиз реже раза в неделю из-за страха сломать что-то в монолите
Технические причины:
- Масштабирование: разные части системы имеют разные профили нагрузки (например, сервис платежей нагружен в 10 раз сильнее, чем сервис отчётов)
- Изоляция отказов: падение одного модуля не должно ронять всю систему
- Разные требования к хранению данных: одним сервисам нужна реляционная БД, другим - key-value или поисковый движок
Признаки, что рано:
- Нет чётких границ между бизнес-доменами - вы не можете определить, где заканчивается один сервис и начинается другой
- Нет опыта работы с распределёнными системами: сетевые сбои, идемпотентность, распределённые транзакции
- Маленькая команда (меньше 5 человек) - overhead на инфраструктуру съест всё время на фичи
- Нет автоматизации: CI/CD, мониторинг, логирование, трассировка - без этого микросервисы превращаются в ад
Trade-off, о которых нужно помнить:
- Сетевые вызовы вместо вызовов в памяти: latency, частичные отказы, retry, timeout
- Распределённые транзакции: saga pattern, outbox, eventual consistency
- Сложность отладки: нужно трассировать запрос через несколько сервисов
- Управление данными: каждый сервис владеет своей БД, join через API
На практике
Для Python-бэкенда (Django/FastAPI) переход обычно выглядит так:
-
Начните с монолита, но с чёткими модулями. Разделите код на домены, запретите импорты между ними на уровне линтера. Это даст вам карту границ для будущей декомпозиции.
-
Выделите первый сервис по принципу "самая больная точка". Обычно это:
- сервис с высокой нагрузкой, который мешает остальным (например, обработка webhook'ов)
- сервис, который меняется чаще всего и блокирует релизы других команд
- сервис с особыми требованиями к инфраструктуре (например, GPU для ML)
-
Используйте strangler fig pattern. Постепенно заменяйте части монолита на вызовы нового сервиса через API. Старый код остаётся, пока новый сервис не покроет все сценарии.
-
Обеспечьте инфраструктуру до перехода:
- контейнеризация (Docker) и оркестрация (Kubernetes или хотя бы docker-compose)
- CI/CD для каждого сервиса отдельно
- централизованные логи (ELK или Loki) и метрики (Prometheus + Grafana)
- distributed tracing (Jaeger или Zipkin)
-
Для Python выбирайте FastAPI для новых сервисов - он лёгкий, async, хорошо документирован. Django тоже подходит, но тяжелее для микросервиса.
Пример кода
Пример выделения сервиса из монолита на FastAPI:
PYTHON# Монолит: Django viewdef create_order(request):# Валидация, работа с БД, отправка email, обновление склада# Всё в одном месте - сложно масштабировать отдельные частиorder = Order.objects.create(...)send_email(order.user.email, "Order created")warehouse.update_stock(order.items)return JsonResponse({"id": order.id})# Микросервис: FastAPI сервис заказов# app/main.pyfrom fastapi import FastAPI, HTTPExceptionfrom pydantic import BaseModelapp = FastAPI()class OrderCreate(BaseModel):user_id: intitems: list[dict]@app.post("/orders")async def create_order(order: OrderCreate):# Только работа с заказом# Отправка событий в Kafka/RabbitMQ для других сервисовorder_id = save_order(order)publish_event("order_created", {"order_id": order_id, "user_id": order.user_id})return {"id": order_id}
PYTHON# Сервис уведомлений (отдельный микросервис)# Слушает события и шлёт emailfrom fastapi import FastAPIfrom kafka import KafkaConsumerapp = FastAPI()consumer = KafkaConsumer("order_created", bootstrap_servers="kafka:9092")@app.on_event("startup")async def consume():for msg in consumer:data = json.loads(msg.value)send_email(data["user_id"], "Order created")
Как отвечать на собеседовании
Структура ответа:
-
Начните с условий, а не с определения. Скажите: "Микросервисы оправданы, когда монолит начинает тормозить развитие - и это всегда про команду и процесс, а не только про технику".
-
Приведите конкретные критерии. Назовите 3-4 признака: размер команды, частота релизов, разный профиль нагрузки, изоляция отказов.
-
Покажите понимание trade-off. Обязательно упомяните распределённые транзакции и сетевые вызовы - это главная цена микросервисов.
-
Предложите стратегию перехода. Strangler fig, выделение одного сервиса, подготовка инфраструктуры. Это покажет, что вы не просто знаете теорию, а умеете планировать.
-
Если спросят про Python - упомяните FastAPI для новых сервисов и сложности с GIL при CPU-bound задачах (нужны отдельные процессы или asyncio).
Что проверяет интервьюер
-
Понимание trade-off, а не заученных плюсов. Кандидат, который говорит только "микросервисы - это круто", не проходит. Хороший ответ - это баланс: когда выгода перевешивает сложность.
-
Умение определять границы сервисов. Проверяется через вопросы про домены, bounded context, общие данные.
-
Опыт с распределёнными системами. Вопросы про сетевые сбои, идемпотентность, саги, outbox pattern.
-
Практический опыт перехода. Интервьюер хочет услышать, как вы это делали: что выделяли первым, какие грабли собрали, как решали проблему распределённых транзакций.
-
Понимание организационной стороны. Микросервисы - это про команды (закон Конвея), а не только про код.
Типичные ошибки
-
"Микросервисы решают проблемы масштабирования". Это не всегда так: часто монолит масштабируется проще, а микросервисы добавляют сетевые накладные расходы.
-
"Перейдём на микросервисы, чтобы ускорить разработку". На старте разработка замедляется: нужно писать больше кода для коммуникации, настраивать инфраструктуру, думать об отказоустойчивости.
-
Не упоминать распределённые транзакции. Если кандидат не знает про saga или outbox - это красный флаг для senior-позиции.
-
Предлагать микросервисы для маленькой команды. Для 2-3 разработчиков это самоубийство: вся энергия уйдёт на инфраструктуру.
-
Игнорировать данные. "Каждый сервис владеет своей БД" - это ключевой принцип, но многие кандидаты предлагают общую БД, что убивает всю изоляцию.
-
Не говорить про мониторинг и трассировку. Без них вы не сможете отлаживать распределённую систему - это обязательный пункт, а не опциональный.
> Похожие задачи по Python
Что произойдет при вызове асинхронной функции без await?
Что такое Depends в FastAPI и для чего он нужен
Работали ли вы с брокерами сообщений, такими как Kafka или RabbitMQ
Какими версиями Python вы пользовались?
> Похожие задачи по backend
Что произойдет при вызове асинхронной функции без await?
Что такое Depends в FastAPI и для чего он нужен
Работали ли вы с брокерами сообщений, такими как Kafka или RabbitMQ
Какими версиями Python вы пользовались?
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью