Backend и архитектура: вопросы на собеседовании

REST, кеширование, очереди, микросервисы, отказоустойчивость, масштабирование и основы system design.

#1APIjunior

Что такое REST? Назовите принципы и типичные ошибки проектирования API.

REST — архитектурный стиль, а не протокол. Основные ограничения: клиент-серверная модель, отсутствие состояния между запросами, кешируемость, единообразие интерфейса, слоистость.

Практические правила:

  • ресурсы — существительные во множественном числе: /users, /users/42/orders;
  • действие выражает метод, а не URL: DELETE /users/42, а не GET /deleteUser?id=42;
  • коды состояния используются по назначению;
  • версионирование — /api/v1/... или через заголовок;
  • пагинация, фильтрация и сортировка — через query-параметры;
  • ответ об ошибке в едином формате с машиночитаемым кодом.

Типичные ошибки: глаголы в URL, всегда 200 OK с полем error в теле, возврат всей коллекции без ограничения, раскрытие внутренних идентификаторов и стектрейсов, отсутствие идемпотентности у операций оплаты.

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

  • Что означает stateless и почему это важно для масштабирования?
  • Когда GraphQL лучше REST?
#2Кешированиеmiddle

Зачем нужен кеш? Какие проблемы он создаёт?

Кеш хранит результат дорогой операции, чтобы не повторять её. Уровни: кеш в памяти процесса, распределённый (Redis, Memcached), HTTP-кеш и CDN, кеш запросов и страниц БД.

Стратегии:

  • Cache-aside (самая частая): приложение читает кеш, при промахе идёт в БД и кладёт результат;
  • Write-through: запись идёт одновременно в кеш и БД — консистентно, но медленнее;
  • Write-behind: запись в кеш, асинхронно в БД — быстро, но рискованно.

Проблемы, о которых нужно упомянуть:

  1. Инвалидация. Устаревшие данные — главная цена кеша. Подходы: TTL, инвалидация по событию, версия в ключе.
  2. Cache stampede. Популярный ключ протух — сотни запросов одновременно бьют в базу. Лечится блокировкой на пересчёт, вероятностным ранним обновлением, «мягким» TTL.
  3. Холодный старт после перезапуска — прогрев.
  4. Вытеснение. Политики LRU, LFU, TTL; память кеша ограничена.
  5. Консистентность с БД — между записью в базу и инвалидацией есть окно.

Главное правило: сначала измерьте, что именно медленно. Кеш, поставленный до профилирования, чаще добавляет багов, чем скорости.

  • Что такое cache stampede и как его предотвратить?
  • Чем LRU отличается от LFU?
#3Архитектураmiddle

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

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

Микросервисы — независимые сервисы со своими БД и релизами. Дают независимость команд, точечное масштабирование и свободу в выборе технологий.

Цена микросервисов, которую часто забывают назвать:

  • сеть ненадёжна: таймауты, повторы, каскадные отказы;
  • нет распределённых транзакций — нужны саги и компенсации, согласованность становится в конечном счёте;
  • отладка требует распределённой трассировки и централизованных логов;
  • инфраструктура: оркестрация, service discovery, CI/CD на десятки репозиториев;
  • неправильно нарезанные границы дают «распределённый монолит» — худший из вариантов.

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

  • Что такое паттерн Сага?
  • Как работает распределённая трассировка?
#4Очередиmiddle

Зачем нужны очереди сообщений? Что такое гарантии доставки?

Очередь развязывает отправителя и получателя во времени.

Задачи, которые она решает:

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

Гарантии доставки:

  • at-most-once — не более одного раза, возможна потеря;
  • at-least-once — не менее одного раза, возможны дубли. Практический стандарт;
  • exactly-once — в распределённой системе недостижимо в общем виде; эмулируется at-least-once плюс идемпотентный потребитель.

Отсюда главное следствие: обработчик обязан быть идемпотентным. Дедупликация по идентификатору сообщения, INSERT ... ON CONFLICT DO NOTHING, проверка текущего состояния перед изменением.

Что ещё спросят: порядок сообщений (гарантируется только внутри партиции), очередь недоставленных сообщений (DLQ) для «ядовитых» сообщений, стратегия повторов с экспоненциальной задержкой. Различие RabbitMQ (брокер с маршрутизацией, сообщение удаляется после подтверждения) и Kafka (лог с отступом, сообщения хранятся и перечитываются) — тоже частый вопрос.

  • Чем Kafka отличается от RabbitMQ?
  • Что такое dead letter queue?
#5Надёжностьmiddle

Как сделать сервис устойчивым к сбоям зависимостей?

Внешний сервис рано или поздно откажет или замедлится. Набор приёмов:

  1. Таймауты на всё. Запрос без таймаута занимает поток навсегда — так один медленный сервис исчерпывает пул соединений и роняет ваш. Это причина каскадных отказов номер один.
  2. Повторы с экспоненциальной задержкой и джиттером. Повторять можно только идемпотентные операции. Без джиттера все клиенты повторяют синхронно и добивают сервис.
  3. Размыкатель цепи (circuit breaker). После серии ошибок перестаём слать запросы и сразу отвечаем ошибкой, периодически пробуя восстановление. Даёт упавшему сервису шанс подняться.
  4. Переборки (bulkhead) — отдельные пулы соединений для разных зависимостей, чтобы отказ одной не исчерпал ресурсы для всех.
  5. Постепенная деградация — отдать кешированные или неполные данные вместо ошибки. Лента без блока рекомендаций лучше, чем пустая страница.
  6. Ограничение частоты запросов и очередь с отбрасыванием, чтобы защититься от перегрузки.
  7. Наблюдаемость: метрики (частота ошибок, задержки по перцентилям), логи с идентификатором запроса, трассировка, алерты по симптомам для пользователя, а не по загрузке процессора.

Упоминание таймаутов и размыкателя цепи сразу выделяет кандидата: это опыт эксплуатации, а не теория.

  • Как работает circuit breaker?
  • Почему важен джиттер при повторах?

Ещё 7 вопросов в этом треке

  1. Как правильно хранить пароли пользователей?
  2. Спроектируйте сервис сокращения ссылок. С чего начать?
  3. Что такое горизонтальное и вертикальное масштабирование? Что такое stateless-сервис?
  4. Как организовать валидацию и обработку ошибок в API?
  5. Что такое CI/CD и Docker? Зачем они нужны?
  6. Что говорит теорема CAP? Как она влияет на выбор хранилища?
  7. Чем WebSocket отличается от long polling и Server-Sent Events? Что выбрать для чата и уведомлений?