> Когда переходить на микросервисы (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) переход обычно выглядит так:

  1. Начните с монолита, но с чёткими модулями. Разделите код на домены, запретите импорты между ними на уровне линтера. Это даст вам карту границ для будущей декомпозиции.

  2. Выделите первый сервис по принципу "самая больная точка". Обычно это:

    • сервис с высокой нагрузкой, который мешает остальным (например, обработка webhook'ов)
    • сервис, который меняется чаще всего и блокирует релизы других команд
    • сервис с особыми требованиями к инфраструктуре (например, GPU для ML)
  3. Используйте strangler fig pattern. Постепенно заменяйте части монолита на вызовы нового сервиса через API. Старый код остаётся, пока новый сервис не покроет все сценарии.

  4. Обеспечьте инфраструктуру до перехода:

    • контейнеризация (Docker) и оркестрация (Kubernetes или хотя бы docker-compose)
    • CI/CD для каждого сервиса отдельно
    • централизованные логи (ELK или Loki) и метрики (Prometheus + Grafana)
    • distributed tracing (Jaeger или Zipkin)
  5. Для Python выбирайте FastAPI для новых сервисов - он лёгкий, async, хорошо документирован. Django тоже подходит, но тяжелее для микросервиса.

Пример кода

Пример выделения сервиса из монолита на FastAPI:

PYTHON
# Монолит: Django view
def 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.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class OrderCreate(BaseModel):
user_id: int
items: 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
# Сервис уведомлений (отдельный микросервис)
# Слушает события и шлёт email
from fastapi import FastAPI
from kafka import KafkaConsumer
app = 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")

Как отвечать на собеседовании

Структура ответа:

  1. Начните с условий, а не с определения. Скажите: "Микросервисы оправданы, когда монолит начинает тормозить развитие - и это всегда про команду и процесс, а не только про технику".

  2. Приведите конкретные критерии. Назовите 3-4 признака: размер команды, частота релизов, разный профиль нагрузки, изоляция отказов.

  3. Покажите понимание trade-off. Обязательно упомяните распределённые транзакции и сетевые вызовы - это главная цена микросервисов.

  4. Предложите стратегию перехода. Strangler fig, выделение одного сервиса, подготовка инфраструктуры. Это покажет, что вы не просто знаете теорию, а умеете планировать.

  5. Если спросят про Python - упомяните FastAPI для новых сервисов и сложности с GIL при CPU-bound задачах (нужны отдельные процессы или asyncio).

Что проверяет интервьюер

  • Понимание trade-off, а не заученных плюсов. Кандидат, который говорит только "микросервисы - это круто", не проходит. Хороший ответ - это баланс: когда выгода перевешивает сложность.

  • Умение определять границы сервисов. Проверяется через вопросы про домены, bounded context, общие данные.

  • Опыт с распределёнными системами. Вопросы про сетевые сбои, идемпотентность, саги, outbox pattern.

  • Практический опыт перехода. Интервьюер хочет услышать, как вы это делали: что выделяли первым, какие грабли собрали, как решали проблему распределённых транзакций.

  • Понимание организационной стороны. Микросервисы - это про команды (закон Конвея), а не только про код.

Типичные ошибки

  • "Микросервисы решают проблемы масштабирования". Это не всегда так: часто монолит масштабируется проще, а микросервисы добавляют сетевые накладные расходы.

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

  • Не упоминать распределённые транзакции. Если кандидат не знает про saga или outbox - это красный флаг для senior-позиции.

  • Предлагать микросервисы для маленькой команды. Для 2-3 разработчиков это самоубийство: вся энергия уйдёт на инфраструктуру.

  • Игнорировать данные. "Каждый сервис владеет своей БД" - это ключевой принцип, но многие кандидаты предлагают общую БД, что убивает всю изоляцию.

  • Не говорить про мониторинг и трассировку. Без них вы не сможете отлаживать распределённую систему - это обязательный пункт, а не опциональный.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью