Перейти к содержимому

Микросервисы или монолит: как ответить на собеседовании

Команда Interview Assistantтехнические вопросыархитектура

Правильного ответа «микросервисы лучше» или «монолит лучше» не существует: выбор зависит от размера и структуры команды, зрелости продукта, требований к независимому деплою и масштабированию и от того, готова ли организация платить за распределённую систему. Для нового продукта и небольшой команды разумная точка старта обычно модульный монолит с чёткими границами модулей, а выделение сервисов оправдано, когда появляется конкретная боль, которую оно снимает. На собеседовании ценят не выбор стороны, а умение назвать критерии, цену каждого варианта и условия, при которых решение поменяется. Ниже разобрано, что проверяет интервьюер, как отвечать коротко и развёрнуто и какие ошибки чаще всего портят ответ.

Что на самом деле проверяет интервьюер

Интервьюер почти никогда не ждёт от вас позиции. Он смотрит на другое:

  • Умение рассуждать через компромиссы. Сильный кандидат говорит «это зависит от…» и сразу перечисляет, от чего именно. Слабый либо выбирает сторону по привычке, либо отделывается «зависит от ситуации» без продолжения.
  • Понимание цены распределённости. Сеть ненадёжна, вызовы медленнее локальных, транзакции между сервисами не бывают бесплатными. Кто видел это в продакшене, говорит об этом сам.
  • Связь архитектуры с организацией. Микросервисы решают во многом организационную задачу: дать командам деплоить независимо. Без этого понимания ответ остаётся чисто техническим и неполным.
  • Опыт. Вопрос часто продолжается фразой «а как было у вас?». Интервьюер хочет услышать реальную историю: что болело, что сделали, что получилось.

На собеседовании с руководителем и на секции system design эта тема всплывает особенно часто, но может прозвучать и как отдельный вопрос на обычном техническом собеседовании.

Определения, с которых стоит начать

Термины часто понимают по-разному, поэтому стоит договориться о них сразу:

  • Монолит — приложение, которое собирается и деплоится как одно целое. Внутри у него может быть хорошая структура, а может быть «большой ком грязи»: это свойство кода, а не архитектуры как таковой.
  • Модульный монолит — один деплой, но код разбит на модули с явными публичными интерфейсами и запретом лезть во внутренности соседей. Часто у каждого модуля своя схема или свой набор таблиц в общей базе.
  • Микросервисы — набор независимо деплоящихся сервисов, каждый владеет своими данными и общается с остальными по сети: синхронно (HTTP, gRPC) или асинхронно через брокер сообщений.
  • Распределённый монолит — худший вариант: сервисы разнесены по сети, но деплоить их можно только вместе, потому что они связаны общей базой или синхронными цепочками вызовов. Это то, во что часто превращается неудачный переход на микросервисы.

Сравнение по критериям

КритерийМонолитМикросервисы
Скорость стартаБыстро: один репозиторий, один деплой, одна базаМедленнее: нужны инфраструктура, CI/CD на каждый сервис, наблюдаемость
Независимый деплой командСложно, все релизятся вместеГлавное преимущество, если границы выбраны удачно
МасштабированиеМасштабируется целиком, копиями всего приложенияМожно масштабировать только нагруженные части
Транзакции и консистентностьОбычные транзакции в одной базеРаспределённые сценарии: саги, outbox, итоговая согласованность
ОтладкаСтектрейс в одном процессеРаспределённая трассировка, корреляция логов между сервисами
ОтказоустойчивостьПадает целикомМожно изолировать сбой, но появляются каскадные отказы по сети
Технологическая свободаОдин стекРазные языки и базы, что и плюс, и источник зоопарка
Рефакторинг границДешёвый: перенос кода внутри проектаДорогой: меняются контракты API и владение данными

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

Цена распределённости

Это та часть ответа, которая сильнее всего отличает опытного кандидата. Стоит назвать конкретные проблемы, а не общую «сложность»:

  • Сеть ненадёжна. Вызов другого сервиса может упасть, зависнуть или выполниться, но не вернуть ответ. Нужны таймауты, ретраи с backoff, идемпотентность операций и circuit breaker, чтобы не устроить каскадный отказ.
  • Задержки складываются. Цепочка из нескольких синхронных вызовов медленнее вызова функции в том же процессе на порядки. Длинные синхронные цепочки — признак неудачных границ.
  • Нет общих транзакций. Заказ, оплата и резерв товара в разных сервисах нельзя записать одной ACID-транзакцией. Используют саги с компенсирующими действиями и паттерн transactional outbox, чтобы событие гарантированно ушло вместе с изменением в базе. Про то, что даёт транзакция в одной базе, подробно в статье про вопросы по SQL.
  • Итоговая согласованность. Данные в разных сервисах какое-то время расходятся, и продукт должен это переживать.
  • Наблюдаемость. Без распределённой трассировки, централизованных логов и метрик найти причину ошибки почти невозможно.
  • Версионирование контрактов. API между сервисами нельзя менять одним коммитом: нужна обратная совместимость и постепенный переход потребителей. Какие тесты ловят такие поломки и где граница между интеграционными и e2e, разобрано в статье про вопросы по тестированию.
  • Операционная нагрузка. Больше деплоев, конфигураций, секретов, сертификатов и дашбордов. Кто-то должен всем этим заниматься.

Когда микросервисы оправданы

Хороший ответ называет условия, а не лозунги:

  1. Несколько команд мешают друг другу. Релизы блокируются, конфликты в общем коде, одна команда ломает другую. Микросервисы с понятным владением снимают эту проблему.
  2. Части системы сильно различаются по нагрузке или требованиям. Например, обработка медиа требует много CPU, а остальной продукт нет. Выделение такого куска позволяет масштабировать его отдельно.
  3. Нужна изоляция сбоев или требований безопасности. Платёжный контур с отдельным доступом и аудитом часто выделяют независимо от остальной архитектуры.
  4. Границы предметной области устоялись. Вы понимаете bounded contexts и знаете, что они не будут переезжать каждый квартал.
  5. Есть платформа. CI/CD, мониторинг, трассировка и автоматизация инфраструктуры уже работают, и новый сервис заводится быстро.

И наоборот: маленькая команда, продукт в поиске рынка, неясные границы и отсутствие DevOps-зрелости — сильные аргументы за монолит.

Модульный монолит как разумная середина

Модульный монолит — то, что стоит упомянуть в любом развёрнутом ответе. Его идея: получить большую часть выгод от разделения на уровне кода, не платя за сеть. На Java-собеседованиях этот вопрос обычно идёт следом за Spring и транзакциями, см. собеседование Java.

  • Модули общаются через публичные интерфейсы, а не через прямой доступ к чужим таблицам и классам.
  • Правила зависимостей можно проверять автоматически: архитектурными тестами или ограничениями сборки.
  • Если модулю потом понадобится отдельный деплой, его границы уже есть, и выделение превращается в механическую работу, а не в переписывание.

Решение о выделении сервиса можно отложить до момента, когда для него появятся данные.

Как раскрывать тему: коротко и развёрнуто

Коротко, секунд за 30: «Зависит от команды и стадии продукта. Монолит проще в разработке, отладке и транзакциях, микросервисы дают независимый деплой и масштабирование частей, но добавляют цену распределённой системы: сеть, согласованность данных, наблюдаемость. Для нового продукта я бы начал с модульного монолита и выделял сервисы, когда появится конкретная причина».

Развёрнуто, минуты за 5:

  1. Уточните контекст: сколько команд, какая нагрузка, какая стадия продукта, есть ли платформа.
  2. Назовите критерии выбора, как в таблице выше.
  3. Расскажите о цене распределённости на двух-трёх конкретных примерах.
  4. Предложите путь: модульный монолит, потом выделение по принципу strangler fig — новый сервис постепенно забирает функциональность, трафик переключается частями, старый код удаляется.
  5. Скажите, как будете выбирать границы: по предметной области (bounded contexts), а не по техническим слоям вроде «сервис базы данных» и «сервис API».
  6. Приведите пример из своего опыта, если он есть.

Если интервьюер просит спроектировать конкретную систему, применяйте тот же подход внутри задачи, как в статье про system design.

Вопрос — слабый ответ — сильный ответ

ВопросСлабый ответСильный ответ
Что лучше: монолит или микросервисы?«Микросервисы, потому что это современно»«Зависит от команды, нагрузки и стадии продукта; вот критерии…»
Как разделить монолит на сервисы?«По слоям: фронт, бэк, база»«По bounded contexts, начиная с наименее связанного модуля, постепенно, через strangler fig»
Как сделать транзакцию между сервисами?«Двухфазный коммит» без оговорок«Чаще сагу с компенсациями и outbox; 2PC возможен, но блокирует ресурсы и плохо переносит сбои»
Можно ли сервисам делить одну базу?«Да, так проще»«Временно при миграции можно, но тогда сервисы связаны схемой и деплоем; цель — каждый владеет своими данными»
Что делать, если сервис-зависимость упал?«Повторять запрос, пока не ответит»«Таймаут, ограниченные ретраи с backoff, circuit breaker, деградация функциональности»

Типичные ошибки и ловушки

  • Выбор стороны без критериев. «Я всегда за микросервисы» звучит как отсутствие опыта с их ценой.
  • «Зависит» без продолжения. Если после этих слов нет списка факторов, ответ пустой.
  • Нарезка по техническим слоям. Сервис «пользователи-БД» и сервис «пользователи-API» — это распределённый монолит.
  • Общая база как постоянное решение. Изменение схемы ломает несколько сервисов сразу, и независимого деплоя больше нет.
  • Микросервис на каждую сущность. Слишком мелкие сервисы порождают длинные цепочки вызовов и бесконечную координацию.
  • Игнорирование организации. Архитектура, которая не совпадает со структурой команд, обычно начинает подстраиваться под неё сама — эту связь называют законом Конвея.
  • Отсутствие наблюдаемости в плане. Предложить микросервисы и не сказать ни слова о логах, метриках и трассировке — заметный пробел.

Чек-лист подготовки

  1. Определения: монолит, модульный монолит, микросервисы, распределённый монолит.
  2. Пять-шесть критериев выбора и сторона, которую каждый из них поддерживает.
  3. Цена распределённости: сеть, задержки, транзакции, согласованность, наблюдаемость, контракты.
  4. Паттерны: saga, transactional outbox, circuit breaker, strangler fig, API gateway.
  5. Как выбирать границы: bounded contexts, слабая связанность, владение данными.
  6. Синхронное и асинхронное взаимодействие, их плюсы и минусы.
  7. Одна история из опыта: что болело, что сделали, чем заплатили, что бы сделали иначе.

В тестовом задании та же логика: аккуратные модули обычно ценятся выше преждевременной россыпи сервисов.

Где здесь помогает Interview Assistant

Вопросы про архитектуру редко звучат одной фразой: интервьюер добавляет контекст, уточняет условия и переходит к следующему вопросу по вашему же ответу. Interview Assistant — приложение для macOS и Windows — транскрибирует речь интервьюера в реальном времени и отвечает сначала краткими тезисами, а затем развёрнутым разбором с подводными камнями: например, напомнит про цену распределённых транзакций или про риск общей базы. Это помощь в структуре ответа, а не замена знаний: рассуждать о компромиссах и рассказывать свой опыт всё равно вам. Первые 20 минут бесплатны — их хватит, чтобы заранее проверить, как приложение слышит ваш звонок.

Частые вопросы

Нужно ли знать микросервисы junior-разработчику?

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

Что ответить, если я работал только с монолитом?

Честно сказать об этом и показать понимание теории: критерии выбора, цену распределённости, модульный монолит. Хорошо добавить, как устроены границы модулей в вашем проекте и где бы вы провели разрез, если бы понадобилось выделить сервис.

Чем модульный монолит отличается от обычного?

У модульного монолита явные границы: модули общаются только через публичные интерфейсы, а правила зависимостей соблюдаются и по возможности проверяются автоматически. В обычном монолите любой код может обратиться к любому другому, и со временем связность растёт.

Обязательно ли каждому сервису иметь свою базу данных?

Принцип «каждый сервис владеет своими данными» нужен, чтобы сервисы деплоились и менялись независимо. Это не обязательно отдельный сервер СУБД: можно разделить схемы или права доступа в одном кластере. Главное, чтобы один сервис не читал и не писал напрямую таблицы другого.

Как понять, что граница сервиса выбрана неудачно?

Признаки: почти любая фича требует изменений в нескольких сервисах сразу, сервисы постоянно синхронно вызывают друг друга и зависят от одних и тех же данных. Это значит, что связность внутри предметной области разрезана, и границу стоит пересмотреть.