Перейти к содержанию
Бэкенд и системы

29 вопросов по теме «архитектура и масштабирование backend» на собеседовании

В этом материале — 29 вопросов из русской колоды RecallDeck по теме «архитектура и масштабирование backend». Сначала сформулируйте короткий ответ сами, затем откройте подробный разбор и проверьте примеры, ограничения и отказные случаи.

22 мин чтения29 подробных ответовПроверено 24 августа 2026
Главная мысль

Стройте ответ вокруг владения состоянием, лимитов, отказа и восстановления. Определение становится инженерным ответом только в конкретном production-сценарии.

Вопросы и ответы

29 подробных ответов

01

Вертикальное vs горизонтальное масштабирование?

Короткий ответ: Вертикальное (scale up) — добавляем ресурсы одной машине (CPU/RAM). Горизонтальное (scale out) — добавляем больше машин и распределяем нагрузку между ними.

Подробно:

  • Вертикальное (scale up/down): взять сервер помощнее. Просто (код не меняется), но есть физический потолок, дорого на верхней границе, и сама машина остаётся single point of failure. Часто требует downtime при апгрейде.
  • Горизонтальное (scale out/in): добавляем узлы за балансировщиком. Практически безграничный потолок, отказоустойчивость (узел упал — остальные живут), линейная стоимость. Но требует stateless-архитектуры, балансировки, распределённого состояния, усложняет консистентность и дебаг.
  • На практике сначала выжимают вертикальное (быстро и дёшево до определённого предела), затем переходят на горизонтальное. БД масштабировать горизонтально труднее всего (отсюда шардирование/реплики).

⚠️ Ловушка: «Просто добавим серверов» не работает, если сервис stateful (хранит сессии/файлы в памяти/на локальном диске). Сначала сделай сервис stateless, потом масштабируй.

02

Stateless vs stateful сервисы?

Короткий ответ: Stateless-сервис не хранит данные между запросами локально — любой запрос может обработать любой инстанс. Stateful хранит состояние (сессию, кэш, файлы) в себе, что привязывает клиента к конкретному инстансу.

Подробно:

  • Почему stateless легче масштабировать: инстансы взаимозаменяемы. Можно добавлять/убирать узлы, рестартовать, делать rolling deploy без потери данных. Балансировщик кидает запрос на любой узел. Не нужны sticky sessions.
  • Где хранить состояние: выносим наружу в общее хранилище:
    • сессии → Redis / Memcached / БД (или вообще JWT — состояние у клиента);
    • файлы/загрузки → объектное хранилище (S3), не локальный диск;
    • кэш → распределённый Redis;
    • очереди задач → брокер (RabbitMQ/Kafka).
  • Сам сервис становится «функцией»: вход → обработка → выход, без внутренней памяти между запросами.

💡 Stateless не значит «без состояния вообще» — состояние просто живёт во внешних системах, разделяемых всеми инстансами.

⚠️ Ловушка: скрытое состояние — in-memory кэш, локальные temp-файлы, in-process планировщики (cron внутри инстанса будет запущен N раз на N инстансах). Всё это ломает горизонтальное масштабирование.

03

Балансировка нагрузки: L4 vs L7?

Короткий ответ: L4-балансировщик работает на транспортном уровне (TCP/UDP, IP+порт), не смотрит в содержимое. L7 — на прикладном (HTTP), видит URL, заголовки, cookie и может маршрутизировать по ним.

Подробно:

  • L4 (транспорт): быстрый, дешёвый по CPU, проксирует пакеты/соединения по IP и порту. Не понимает HTTP. Примеры: AWS NLB, IPVS, HAProxy в TCP-режиме.
  • L7 (приложение): разбирает HTTP, умеет content-based routing (/api → один пул, /static → другой), терминирует TLS, добавляет заголовки, делает retry/circuit-breaking. Дороже по ресурсам. Примеры: nginx, AWS ALB, Envoy, HAProxy в HTTP-режиме.

Алгоритмы балансировки:

  • Round-robin — по кругу, поровну. Хорошо при одинаковых запросах/серверах.
  • Weighted round-robin — с весами (мощный сервер получает больше).
  • Least connections — на узел с наименьшим числом активных соединений. Хорошо для разной длительности запросов.
  • IP hash — узел выбирается по хэшу IP клиента → один клиент всегда на один узел (примитивная «липкость»).
  • Least response time / random two choices — продвинутые варианты.

Health checks: балансировщик периодически пингует узлы (active: HTTP /health; passive: смотрит на ошибки реального трафика). Нездоровый узел выводится из пула, чтобы трафик не шёл на мёртвый инстанс.

Sticky sessions (session affinity): клиента «приклеивают» к одному узлу (по cookie или IP hash), чтобы локальная сессия не терялась. Это костыль для stateful-сервисов — лучше сделать stateless + внешнее хранилище сессий.

⚠️ Ловушка: sticky sessions ломают равномерность нагрузки и отказоустойчивость — если узел упал, все приклеенные к нему клиенты теряют сессию. Не лечит проблему, а откладывает.

04

Reverse proxy (nginx) vs load balancer?

Короткий ответ: Это пересекающиеся, но разные роли. Reverse proxy — посредник перед сервером(ами): TLS, кэш, сжатие, маршрутизация. Load balancer — распределение нагрузки между несколькими бэкендами. Один продукт (nginx) умеет и то, и другое.

Подробно:

  • Reverse proxy скрывает бэкенд от клиента и берёт на себя кросс-функции: терминацию TLS, gzip/brotli, кэширование статики и ответов, заголовки безопасности, rate limiting, отдачу статических файлов, буферизацию. Может стоять перед одним сервером.
  • Load balancer — частный случай/функция: раскидывает запросы по пулу, делает health checks. Может быть L4 (без знания HTTP).
  • Разница в фокусе: LB отвечает на вопрос «на какой из N узлов», reverse proxy — «как обработать/преобразовать запрос перед бэкендом». nginx/Envoy/HAProxy совмещают обе роли.

💡 На собеседовании: «nginx как reverse proxy + балансировщик L7 перед stateless-инстансами приложения, TLS терминируется на нём».

⚠️ Ловушка: не путай с forward proxy — тот стоит на стороне клиента и проксирует исходящий трафик клиента наружу (например, корпоративный прокси). Reverse proxy стоит на стороне сервера.

05

Кэширование на всех уровнях?

Короткий ответ: Кэш есть на каждом слое пути запроса: браузер → CDN → reverse proxy → приложение → БД. Чем ближе к клиенту, тем дешевле и быстрее, но тем сложнее инвалидация.

Подробно (слои сверху вниз):

  1. Браузер — HTTP-кэш по заголовкам Cache-Control, ETag, Last-Modified. Самый дешёвый: запрос вообще не уходит.
  2. CDN / edge — кэширует статику и кэшируемые ответы ближе к пользователю географически.
  3. Reverse proxy (nginx) — кэш ответов/страниц на стороне дата-центра, разгружает приложение.
  4. Приложение — in-memory (локальный, быстрый, но не разделяемый) и распределённый (Redis/Memcached — общий для всех инстансов). Кэширование результатов вычислений, сессий, агрегатов.
  5. БД — buffer pool/page cache, query plan cache, материализованные представления.

Стратегии: cache-aside (приложение само читает/пишет кэш — самая частая), write-through, write-behind, read-through. Ключевые проблемы: инвалидация, TTL, cache stampede (много промахов разом → защита через locks/SETNX), консистентность кэша с БД.

📎 Подробности по Redis (структуры, паттерны, eviction, persistence) — см. отдельный redis-файл.

⚠️ Ловушка: «Phil Karlton: в CS две сложные вещи — инвалидация кэша и именование». Кэширование решает чтение, но добавляет проблему устаревших данных. Не кэшируй то, что должно быть строго консистентным, без продуманной инвалидации.

06

CDN: что это и зачем?

Короткий ответ: Content Delivery Network — географически распределённая сеть edge-серверов, кэширующих контент ближе к пользователю, чтобы снизить латентность и разгрузить origin.

Подробно:

  • Что кэширует: статику (картинки, CSS/JS, видео), а с edge-логикой — и динамические/API-ответы.
  • Зачем: меньше RTT (контент физически ближе), разгрузка origin-сервера, защита от DDoS, TLS-терминация на краю, абсорбция пиков трафика.
  • Edge: точки присутствия (PoP) по миру; запрос идёт на ближайший edge, при промахе — на origin, ответ кэшируется. Edge computing — выполнение лёгкого кода на краю (Cloudflare Workers, Lambda@Edge).
  • Инвалидация — через purge/версионирование URL (хэш в имени файла → app.a1b2c3.js).

⚠️ Ловушка: не клади приватные/персональные данные в публичный CDN-кэш без Cache-Control: private. Иначе один пользователь увидит закэшированный ответ другого.

07

Зачем нужны очереди сообщений?

Короткий ответ: Чтобы сделать взаимодействие асинхронным, развязать сервисы, сгладить пики нагрузки и повысить надёжность доставки работы.

Подробно — четыре главных мотива:

  1. Асинхронность: запрос вернул ответ сразу, тяжёлая работа (отправка email, генерация отчёта, обработка видео) идёт в фоне. Пользователь не ждёт.
  2. Развязка (decoupling): producer не знает про consumer и не зависит от его доступности/скорости. Можно деплоить, масштабировать, менять консьюмеров независимо.
  3. Сглаживание пиков (load leveling / buffering): всплеск трафика наполняет очередь, консьюмеры разгребают в своём темпе. БД/сервис не захлёбывается.
  4. Надёжность: сообщение сохранено в брокере; если консьюмер упал — сообщение не потеряется и будет обработано позже (с ack/retry).

Бонус: масштабирование консьюмеров (добавил воркеров — разгрёб быстрее), буфер между быстрым и медленным компонентом.

⚠️ Ловушка: очередь не бесплатна — добавляет инфраструктуру, eventual consistency, сложность отладки и требование идемпотентности консьюмеров. Не вставляй очередь там, где нужен синхронный ответ здесь и сейчас.

08

RabbitMQ vs Kafka vs Redis/SQS?

Короткий ответ: RabbitMQ — классический брокер (умная маршрутизация, сообщение удаляется после обработки). Kafka — распределённый лог событий (сообщения хранятся, читаются по offset, отлично для стриминга/больших объёмов). Redis (Streams/Pub-Sub) — простой и быстрый, в одном процессе с кэшем. SQS — managed-очередь в AWS, минимум эксплуатации.

Подробно — broker vs log (ключевое различие):

  • Брокер (RabbitMQ, SQS): push/pull сообщения, брокер ведёт состояние доставки; после ack сообщение удаляется. Богатая маршрутизация (exchanges, routing keys, priority). Очередь — обычно один потребитель на сообщение (work queue).
  • Лог (Kafka): сообщения append-only пишутся в партиции и хранятся (по retention/размеру), даже после чтения. Консьюмеры держат свой offset — могут перечитать историю, разные группы читают независимо. Порядок гарантируется внутри партиции. Масштабируется на огромный throughput.

Когда что:

  • RabbitMQ — таски, RPC, сложная маршрутизация, относительно умеренный объём, нужны per-message гарантии и приоритеты.
  • Kafka — event sourcing, аналитика/стриминг, лог изменений, миллионы сообщений/сек, нужна перечитка и многим потребителям одно событие.
  • Redis Streams — лёгкие очереди, когда Redis уже есть; меньше durability-гарантий, проще.
  • SQS — «не хочу администрировать брокер», AWS-стек; standard (at-least-once, без строгого порядка) или FIFO (порядок + дедуп).

⚠️ Ловушка: Kafka — не «лучшая очередь», а другой инструмент. Для классической work-queue с ack/retry/приоритетами RabbitMQ удобнее. Kafka силён там, где нужны лог, перечитка и стриминг. Выбор по задаче, а не «модно».

09

Гарантии доставки: at-most- / at-least- / exactly-once?

Короткий ответ: at-most-once — максимум один раз (возможна потеря). at-least-once — минимум один раз (возможны дубли). exactly-once — ровно один раз (дорого и условно, обычно достигается at-least-once + идемпотентность/дедупликация).

Подробно:

  • At-most-once: отправил и забыл, без ретраев. Сообщение может потеряться, но дублей нет. Подходит для метрик/телеметрии, где потеря не критична.
  • At-least-once: ack + ретраи. Если ack не дошёл — повтор. Дубли возможны (обработали, упали до ack → переотправка). Самый распространённый дефолт.
  • Exactly-once: «святой грааль». В чистом виде в распределёнке невозможен из-за сбоев сети; на практике эмулируется: at-least-once доставка + идемпотентный консьюмер или дедупликация по message_id. Kafka даёт exactly-once внутри своей экосистемы (транзакции producer→topic→consumer-offset), но при выходе наружу (запись в стороннюю БД) снова нужна идемпотентность.

Идемпотентные консьюмеры: обработка одного и того же сообщения N раз даёт тот же эффект, что и один раз. Реализация: уникальный message_id → хранилище обработанных ID / UPSERT по бизнес-ключу / условные апдейты. Это правильный способ «прожить» дубли.

Dead Letter Queue (DLQ): сообщения, которые не удалось обработать после N ретраев (или с истёкшим TTL, или невалидные), уходят в отдельную очередь. Там их разбирают вручную/алертом, не теряя и не блокируя основную очередь «ядовитыми» сообщениями (poison messages).

⚠️ Ловушка: «у нас exactly-once» почти всегда означает «at-least-once + идемпотентность». Если консьюмер не идемпотентен, любые ретраи (а они будут) приведут к дублированным эффектам: два списания денег, два письма.

10

Pub/Sub vs очередь (work queue)?

Короткий ответ: В work queue сообщение получает один консьюмер (конкурирующие воркеры делят работу). В pub/sub сообщение получают все подписчики (broadcast).

Подробно:

  • Work queue (point-to-point): N воркеров на одной очереди делят сообщения — каждое уходит ровно одному. Цель — распараллелить обработку. Пример: очередь задач Celery, обработка заказов.
  • Pub/Sub (publish-subscribe): publisher шлёт в topic, каждый подписчик получает копию. Цель — оповестить много независимых потребителей. Пример: событие «заказ создан» → биллинг, склад, аналитика, нотификации, каждый делает своё.
  • В RabbitMQ это настраивается через exchange (fanout = pub/sub, direct/topic-routing). В Kafka каждая consumer group получает все сообщения (pub/sub между группами), а внутри группы партиции делятся между консьюмерами (work queue).

⚠️ Ловушка: если повесить несколько разных по смыслу обработчиков на одну work-queue, они начнут «воровать» сообщения друг у друга. Для broadcast нужен fanout/topic, а не общая очередь.

11

Celery (Python) — как устроен?

Короткий ответ: Celery — фреймворк для фоновых задач в Python. Задачи (@app.task) кладутся в брокер (RabbitMQ/Redis), воркеры их забирают и выполняют; результат опционально пишется в result backend.

Подробно:

  • Компоненты:
    • Задача (task): функция, помеченная @app.task, вызывается через .delay() / .apply_async() — это публикует сообщение в брокер.
    • Брокер (broker): транспорт сообщений (RabbitMQ предпочтителен, Redis популярен для простоты).
    • Воркер (worker): процесс, который слушает очередь и исполняет задачи; масштабируется числом процессов/потоков и количеством воркеров.
    • Result backend: хранилище результатов/статусов (Redis, БД) — опционально, по умолчанию результат можно не хранить.
  • Когда использовать: отправка email/SMS, генерация отчётов/PDF, обработка медиа, вызовы медленных внешних API, ретраи с backoff — всё, что не должно блокировать HTTP-ответ.
  • Периодические задачи: Celery Beat — планировщик (cron-подобный), который по расписанию кидает задачи в очередь (один инстанс Beat на кластер, чтобы не дублировать!).
  • Возможности: retries (max_retries, retry_backoff), acks_late (ack после выполнения — at-least-once), маршрутизация по очередям, цепочки/группы (canvas: chain, group, chord).

⚠️ Ловушка: задачи Celery должны быть идемпотентны — при acks_late и падении воркера задача переисполнится. И не запускай несколько инстансов Celery Beat — получишь дублирование периодических задач.

12

Синхронное vs асинхронное взаимодействие сервисов?

Короткий ответ: Синхронно — вызывающий блокируется и ждёт ответа (REST/gRPC запрос-ответ). Асинхронно — отправил сообщение/событие и не ждёт, ответ (если нужен) приходит позже или через callback/событие.

Подробно:

  • Синхронно: просто, понятно, немедленный результат и ошибка. Но создаёт temporal coupling — оба сервиса должны быть живы одновременно; падение/тормоза callee бьют по caller; цепочки синхронных вызовов множат латентность и риск каскадных сбоев.
  • Асинхронно (через очередь/событие): развязка во времени, устойчивость к недоступности, сглаживание пиков. Но eventual consistency, сложнее отслеживать поток, нужен обработчик «а где результат».
  • Правило: синхронно — когда нужен немедленный ответ для пользователя (получить профиль). Асинхронно — когда работа может подождать или важна развязка (после оформления заказа: уведомления, начисление бонусов).

⚠️ Ловушка: длинные синхронные цепочки A→B→C→D — антипаттерн: общая латентность складывается, а доступность перемножается (0.99⁴ ≈ 0.96). Часть переводят в асинхронные события.

13

Микросервисы vs монолит?

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

Подробно:

  • Монолит — плюсы: простота разработки/деплоя/отладки, транзакции в одной БД, нет сетевых вызовов между модулями, рефакторинг легче. Минусы: масштабируется только целиком, одна точка отказа на уровне процесса, большая кодовая база, рискованные деплои, привязка к одному стеку.
  • Микросервисы — плюсы: независимый деплой/масштабирование/выбор стека по сервису, изоляция отказов, команды владеют сервисами автономно. Минусы: распределённые транзакции и консистентность, сетевые сбои и латентность, сложный observability/деплой/тестирование, нужна зрелая инфраструктура (CI/CD, мониторинг, оркестрация).
  • Когда что: начинай с (модульного) монолита — большинству он подходит долго. Микросервисы оправданы при больших командах, разной нагрузке на части системы, разных требованиях к масштабированию/релизам. «Modular monolith» — отличный компромисс.
  • Размер сервиса: вокруг бизнес-возможности / bounded context (DDD), своя БД, владеет одна команда. Не «по таблице» и не «нанослужбы».

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

⚠️ Ловушка: микросервисы — это организационное решение и инфраструктурный налог, а не «модно/масштабируемо по умолчанию». Без зрелого DevOps они замедлят команду, а не ускорят.

14

Межсервисное взаимодействие?

Короткий ответ: Синхронно — REST или gRPC (запрос-ответ). Асинхронно — события через брокер. Снаружи — API Gateway. Сетевые кросс-функции — опционально service mesh.

Подробно:

  • REST (HTTP/JSON): универсально, человекочитаемо, кэшируемо, простой дебаг. Медленнее, многословно.
  • gRPC (HTTP/2 + protobuf): бинарно, быстро, строгий контракт (.proto), стриминг, кодогенерация. Хуже для браузера/дебага. Хорош для внутреннего трафика high-throughput.
  • Асинхронные события: публикация событий в брокер (Kafka/RabbitMQ) — максимальная развязка, event-driven архитектура.
  • API Gateway: единая точка входа для клиентов: маршрутизация к сервисам, аутентификация, rate limiting, агрегация ответов, версионирование, TLS. Прячет внутреннюю топологию.
  • Service mesh (кратко): инфраструктурный слой (Istio/Linkerd, sidecar-прокси Envoy) для service-to-service трафика: mTLS, retries, timeouts, circuit breaking, трейсинг, балансировка — вынесены из кода в платформу. Нужен при большом числе сервисов.

⚠️ Ловушка: API Gateway (north-south, клиент↔система) ≠ service mesh (east-west, сервис↔сервис). Разные задачи, часто используются вместе.

15

Согласованность данных в распределёнке?

Короткий ответ: Без общей БД нельзя сделать единую ACID-транзакцию между сервисами. Варианты: 2PC (сильная, но хрупкая), Saga (последовательность локальных транзакций с компенсациями), eventual consistency, Outbox для надёжной публикации событий.

Подробно:

  • 2PC (two-phase commit): координатор → prepare всем участникам → если все ok, commit. Даёт атомарность, но блокирующий, координатор — single point of failure, плохо масштабируется, увеличивает латентность. На практике в микросервисах избегают.
  • Saga pattern: бизнес-транзакция = цепочка локальных транзакций в разных сервисах; при сбое шага запускаются компенсирующие действия (отменить предыдущие). Два стиля:
    • Хореография: сервисы реагируют на события друг друга (без центра). Гибко, но трудно отследить общий поток.
    • Оркестрация: центральный оркестратор управляет шагами и компенсациями. Понятнее, но появляется координатор.
  • Eventual consistency: данные временно расходятся, но сходятся со временем. Достаточно для большинства бизнес-кейсов (счётчики, ленты, рекомендации).
  • Outbox pattern: проблема dual write — нельзя атомарно записать в БД и опубликовать событие. Решение: в одной локальной транзакции пишем и данные, и запись в таблицу outbox; отдельный процесс (poller / CDC, например Debezium) читает outbox и публикует события в брокер. Гарантирует «записал → событие будет опубликовано» (at-least-once).

📎 CAP/консистентность БД — см. nosql-файл.

⚠️ Ловушка: dual write (записать в БД и тут же отправить в Kafka двумя операциями) — классический баг: если упадём между ними, данные и события разойдутся. Лечится Outbox или transactional log tailing (CDC).

16

CAP теорема (кратко)?

Короткий ответ: При сетевом разделении (Partition) распределённая система может сохранить либо Consistency, либо Availability, но не обе. P неизбежно в сети, поэтому реальный выбор — CP или AP.

Подробно:

  • C — все узлы видят одни и те же данные (линеаризуемость). A — каждый запрос получает ответ. P — система работает при потере связи между узлами.
  • Поскольку сетевые сбои случаются, P обязательно → выбираем: при partition либо отвечать возможно устаревшими данными (AP), либо отказывать ради консистентности (CP).
  • Дополнение — PACELC: при Partition выбор A/C, иначе (Else) — компромисс между Latency и Consistency.

📎 Детально с примерами БД (Mongo, Cassandra, etc.) — см. nosql-файл.

⚠️ Ловушка: CAP не про «выбери 2 из 3 навсегда». P нельзя «выбрать не иметь» — это свойство сети. Выбор делается только в момент partition.

17

Идемпотентность операций и дедупликация?

Короткий ответ: Идемпотентная операция при повторном выполнении даёт тот же результат, что и единичное. Дедупликация — отбрасывание повторных сообщений/запросов по уникальному ключу.

Подробно:

  • Зачем: в распределёнке ретраи неизбежны (таймауты, переотправки очереди, повтор пользователем). Без идемпотентности повтор = дубль эффекта (двойное списание).
  • Идемпотентность по HTTP: GET/PUT/DELETE идемпотентны по семантике, POST — нет. Поэтому для POST используют Idempotency-Key (клиент шлёт уникальный ключ, сервер запоминает результат первого вызова и возвращает его на повторах).
  • Способы достижения:
    • UPSERT / условные апдейты по бизнес-ключу;
    • таблица обработанных ID (processed_messages) — проверяем перед обработкой;
    • natural idempotency: «установить статус = X» вместо «инкремент».
  • Дедупликация: на уровне брокера (SQS FIFO dedup window, Kafka producer idempotence) или приложения (по message_id в хранилище с TTL).

⚠️ Ловушка: инкремент (balance += 100) не идемпотентен — повтор удвоит эффект. «Установить значение» или «провести транзакцию с уникальным id» — идемпотентны. Проектируй операции идемпотентными с самого начала.

18

Rate limiting?

Короткий ответ: Ограничение частоты запросов для защиты от перегрузки/abuse. Основные алгоритмы: token bucket, leaky bucket, fixed window, sliding window.

Подробно — алгоритмы:

  • Token bucket: ведро на N токенов, пополняется со скоростью r/сек; запрос тратит токен. Допускает всплески до размера ведра, при этом держит среднюю скорость. Самый популярный (гибкий).
  • Leaky bucket: запросы — вода, «утекает» с постоянной скоростью; переполнение → отказ. Сглаживает трафик до ровного потока, всплески не пропускает.
  • Fixed window: счётчик за фиксированное окно (например, 100/мин). Просто, но проблема границы — двойной всплеск на стыке окон (200 запросов за пару секунд вокруг границы).
  • Sliding window log / counter: скользящее окно (точный лог меток времени или взвешенный counter) — точнее на границах, чуть дороже по памяти/вычислениям.

Где применять: API gateway, на пользователя/IP/ключ API, на дорогие эндпоинты, защита downstream-сервисов и БД. Часто реализуют в Redis (атомарные счётчики/Lua). Ответ при превышении — HTTP 429 + Retry-After.

⚠️ Ловушка: fixed window даёт всплеск на границе окон (до 2× лимита). Для строгих лимитов бери sliding window / token bucket. И помни про распределённость: счётчик должен быть общим (Redis), а не локальным на каждом инстансе.

19

Шардирование и репликация БД?

Короткий ответ: Шардирование — горизонтальное разбиение данных по нескольким БД (масштаб записи/объёма). Репликация — копии данных на других узлах (масштаб чтения и отказоустойчивость). Разделение чтения/записи и CQRS дополняют это.

Подробно:

  • Шардирование (sharding): данные разрезаются по shard key на независимые БД:
    • по ключу/диапазону (range): простой поиск по диапазонам, но риск hot shard;
    • по хэшу ключа (hash): равномерно, но диапазонные запросы дорогие;
    • по справочнику (directory): гибко, но lookup-таблица — точка отказа.
    • Hot partition / hot shard: неудачный ключ → один шард получает непропорционально много трафика (например, шардирование по стране, где 80% юзеров). Выбирай ключ с высокой кардинальностью и равномерностью.
    • Ребалансировка: при добавлении шарда данные надо перераспределить. Наивный hash % N ломается при смене N → используют consistent hashing или виртуальные бакеты, чтобы двигать минимум данных.
  • Репликация: primary (запись) + read replicas (чтение). Даёт масштаб чтения, бэкап, отказоустойчивость. Реплики отстают (replication lag) → eventual consistency на чтении.
  • Разделение чтения/записи: писать в primary, читать с реплик. Бьёт по lag — после записи чтение с реплики может вернуть старое (read-your-writes решают «sticky read с primary» какое-то время).
  • CQRS (кратко): Command Query Responsibility Segregation — разделение модели записи (commands) и модели чтения (queries), часто с отдельными хранилищами, оптимизированными под свои паттерны. Усложняет, оправдан при сильно разной нагрузке чтения/записи.

⚠️ Ловушка: шардирование — дорогая и почти необратимая операция; кросс-шард JOIN/транзакции/агрегации болезненны. Сначала выжми вертикаль, индексы, кэш и read replicas. Шардируй, только когда реально упёрся в запись/объём, и тщательно выбирай shard key.

20

Single point of failure и failover?

Короткий ответ: SPOF — компонент, отказ которого роняет всю систему. Лечится резервированием (репликация, несколько инстансов, мульти-AZ) и failover — автоматическим переключением на резерв.

Подробно:

  • Поиск SPOF: одиночный сервер, одна БД без реплик, один балансировщик, одна зона доступности, единственный брокер, общий кэш без репликации.
  • Устранение:
    • Репликация: копии данных/сервисов (БД primary+replicas, несколько инстансов приложения).
    • Избыточность: N+1 инстансов, балансировщик распределяет; сам балансировщик тоже дублируют.
    • Failover: при отказе primary автоматически промоутится реплика (с health-check и кворумом, чтобы избежать split-brain).
    • Multi-AZ / multi-region для географической устойчивости.
  • Метрики: RTO (за сколько восстановимся) и RPO (сколько данных можем потерять).

⚠️ Ловушка: «у нас две реплики» бесполезно без авто-failover и регулярного теста переключения. Непроверенный failover часто не срабатывает в реальный инцидент. И берегись split-brain — нужен кворум/арбитр.

21

Circuit breaker, retries, timeouts, bulkhead?

Короткий ответ: Паттерны защиты от каскадных сбоев. Timeout — не ждать вечно. Retry с backoff+jitter — повторять с умом. Circuit breaker — перестать долбить мёртвый сервис. Bulkhead — изолировать ресурсы, чтобы сбой одной части не утопил всё.

Подробно:

  • Timeouts: на каждый внешний вызов обязателен таймаут. Без него зависший downstream держит потоки/соединения caller'а → исчерпание пула → падение. Таймауты должны убывать вниз по цепочке.
  • Retries + exponential backoff + jitter:
    • повторять только идемпотентные/безопасные операции и только при ретраибельных ошибках (5xx, таймаут), не при 4xx;
    • exponential backoff: интервалы растут (1с, 2с, 4с…), чтобы не добивать сервис;
    • jitter: случайная добавка к интервалу, чтобы клиенты не ретраили синхронно (thundering herd / retry storm);
    • ограничить число попыток.
  • Circuit breaker: считает ошибки; при превышении порога «размыкается» (Open) — мгновенно фейлит запросы, не нагружая мёртвый сервис; через паузу переходит в Half-Open (пробный запрос) → Closed при успехе. Даёт downstream восстановиться и быстро отдаёт fallback.
  • Bulkhead: изоляция ресурсов (отдельные пулы потоков/соединений на разные зависимости), как переборки в корабле. Сбой/тормоза одной зависимости не исчерпывают общий пул и не топят остальные функции.

⚠️ Ловушка: retry без backoff+jitter и без circuit breaker превращает мелкий сбой в retry storm, который добивает уже шатающийся сервис (каскадный отказ). Ретраи без идемпотентности → дубли. Комбинируй: timeout + ограниченный retry с jitter + circuit breaker.

22

Graceful degradation и backpressure?

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

Подробно:

  • Graceful degradation: если рекомендации недоступны — показать дефолтный список; если кэш упал — идти в БД медленнее; если платёжный провайдер лежит — принять заказ в статусе pending. Fallback-значения, feature flags, отключение второстепенного под нагрузкой (load shedding).
  • Backpressure: потребитель не справляется → должен замедлить/остановить производителя, а не копить бесконечно (что приведёт к OOM). Механизмы: ограниченные очереди и буферы, блокировка/отказ при заполнении, reactive streams (request(n)), HTTP 429/503, TCP flow control. В очередях — естественный буфер + лимиты + DLQ.

⚠️ Ловушка: неограниченные очереди/буферы — иллюзия отказоустойчивости: вместо отказа сервис копит память и падает по OOM (или раздувает латентность). Лучше явный отказ/деградация (fail fast / shed load), чем тихое накопление.

23

12-factor app (кратко)?

Короткий ответ: Набор из 12 практик для создания cloud-native приложений: легко деплоить, масштабировать горизонтально и эксплуатировать.

Подробно — ключевые факторы (на собеседование):

  1. Codebase — один кодбейс в VCS, много деплоев.
  2. Dependencies — явно объявлены и изолированы.
  3. Config — в переменных окружения, не в коде.
  4. Backing services — БД/кэш/брокер как присоединяемые ресурсы (по URL/конфигу).
  5. Build, release, run — строго разделённые стадии.
  6. Processes — приложение как stateless процессы (состояние — во внешних сервисах). ← основа масштабирования.
  7. Port binding — сервис сам экспортирует HTTP, self-contained.
  8. Concurrency — масштаб через процессы (горизонтально).
  9. Disposability — быстрый старт и graceful shutdown (важно для rolling-деплоя/автоскейла).
  10. Dev/prod parity — окружения максимально похожи.
  11. Logs — как поток событий в stdout, агрегируются снаружи.
  12. Admin processes — разовые задачи (миграции) как one-off процессы.

⚠️ Ловушка: самые часто нарушаемые — конфиг в коде (фактор 3) и состояние в процессе (фактор 6). Именно они мешают горизонтальному масштабированию и контейнеризации.

24

Observability: логи, метрики, трейсинг?

Короткий ответ: Три кита наблюдаемости — логи (что произошло), метрики (агрегированные числа во времени), трейсинг (путь запроса через сервисы). Связывает их correlation/trace ID.

Подробно:

  • Логи (logs): дискретные события, желательно структурированные (JSON), агрегируются (ELK/Loki). Для деталей конкретного события/ошибки.
  • Метрики (metrics): числовые ряды (RPS, латентность p50/p95/p99, error rate, насыщение ресурсов). Дёшевы, агрегируемы, для дашбордов и алертов (Prometheus/Grafana). Метод: RED (Rate, Errors, Duration) и USE (Utilization, Saturation, Errors).
  • Трейсинг (distributed tracing): один запрос проходит через много сервисов; trace = дерево spans с таймингами. Видно, где в цепочке узкое место. OpenTelemetry, Jaeger, Zipkin.
  • Correlation ID / trace ID: уникальный идентификатор, присваиваемый запросу на входе (API gateway) и пробрасываемый через все сервисы/логи/события. Позволяет собрать всю картину одного запроса в распределённой системе.

💡 Логи и метрики говорят, что сломалось; трейсинг — где в цепочке.

⚠️ Ловушка: без correlation id логи распределённой системы бесполезны — невозможно связать события одного запроса между сервисами. Прокидывай trace id с самого начала, включая асинхронные сообщения в очередях.

25

Blue-green / canary деплой?

Короткий ответ: Стратегии безопасного выката. Blue-green — два полных окружения, мгновенное переключение трафика. Canary — новая версия получает сначала маленькую долю трафика, постепенно растущую.

Подробно:

  • Blue-green: blue (текущая) и green (новая) работают параллельно; после проверки green балансировщик переключает 100% трафика на неё. Мгновенный откат (переключить обратно), нулевой downtime. Минус — двойные ресурсы и сложности с миграциями БД.
  • Canary: новую версию выкатывают на 1–5% трафика, следят за метриками (ошибки, латентность); при успехе постепенно повышают до 100%, иначе откатывают. Минимизирует радиус поражения. Требует хорошего мониторинга и маршрутизации по долям.
  • Rolling update: инстансы обновляются по очереди (дефолт в k8s) — без двойных ресурсов, но откат медленнее и временно сосуществуют версии.

⚠️ Ловушка: при любой стратегии миграции БД должны быть обратно совместимы — старая и новая версии кода какое-то время работают с одной схемой одновременно. Используй expand-and-contract (сначала добавить, потом удалить колонку в отдельном релизе).

26

Зачем очередь, если можно вызвать сервис напрямую?

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

Подробно: при прямом вызове caller ждёт, оба должны быть живы одновременно, всплеск трафика бьёт прямо в downstream, а если downstream лежит — операция теряется или падает. Очередь: ответ мгновенный, downstream может быть временно недоступен (сообщение подождёт), пик сглаживается буфером, доставка надёжна (ack/retry/DLQ), консьюмеры масштабируются независимо. Цена — eventual consistency и требование идемпотентности. Очередь нужна, когда результат не обязан вернуться немедленно и важна устойчивость; прямой вызов — когда нужен ответ здесь и сейчас.

27

Почему stateless важно для масштабирования?

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

Подробно: если инстанс хранит состояние (сессию, кэш, файлы) в себе, клиента приходится «приклеивать» (sticky sessions), а падение узла теряет данные и ломает балансировку. Stateless выносит состояние в общие хранилища (Redis, S3, БД) → балансировщик кидает запрос куда угодно, rolling-деплой и автоскейл проходят без потерь, горизонтальное масштабирование становится тривиальным «добавь узлов». Это прямо отражено в 12-factor (фактор 6: processes).

28

Микросервисы — когда это ошибка?

Короткий ответ: Когда нет зрелой инфраструктуры/команды, когда домен не понят, или когда выгоды не перевешивают распределённую сложность — особенно на старте продукта.

Подробно: микросервисы — ошибка, если: маленькая команда (накладные расходы съедят пользу); нет CI/CD, мониторинга, оркестрации (операционный ад); границы доменов ещё не ясны (придётся постоянно перекраивать сервисы — дорого через сеть); система требует кросс-сервисных транзакций повсюду; результат — distributed monolith (связанные сервисы с общей БД и совместным деплоем — минусы обоих миров). Правильный путь: начать с модульного монолита, выделять сервисы только когда появилась реальная причина (разная нагрузка/релизный темп/команды).

29

Что отвечать на «как масштабировать этот сервис»?

Короткий ответ: Двигаться по слоям: измерь узкое место → сделай stateless → масштабируй горизонтально за балансировщиком → кэшируй → асинхронь тяжёлое через очереди → масштабируй данные (реплики, потом шардинг) → добавь отказоустойчивость и observability.

Подробно — структура ответа:

  1. Сначала измерь: найди bottleneck (CPU, БД, IO, внешний API) метриками — не оптимизируй вслепую.
  2. Stateless: вынеси сессии/состояние/файлы наружу (Redis/S3), чтобы можно было масштабировать горизонтально.
  3. Горизонтально + LB: N инстансов за L7-балансировщиком с health checks.
  4. Кэширование: браузер/CDN/reverse proxy/Redis — снять нагрузку чтения.
  5. Асинхронность: тяжёлые операции — в очередь к воркерам (Celery/брокер), сгладить пики.
  6. Данные: read replicas + разделение чтения/записи; индексы; при упоре в запись/объём — шардирование с продуманным ключом.
  7. Устойчивость: убрать SPOF, timeouts + retries(backoff+jitter) + circuit breaker + bulkhead, graceful degradation, autoscaling.
  8. Observability: метрики/логи/трейсинг с correlation id, чтобы видеть эффект и новые узкие места.

💡 Ключевая мысль: масштабирование — итеративный процесс по данным метрик, а не разовое «впихнуть Kafka и Kubernetes».

Источники

Источники и редакционная политика

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

От чтения к воспроизведению

Отрепетируйте полный цикл интервью.

RecallDeck возвращает сложные темы по расписанию и помогает удерживать в памяти язык, SQL, архитектуру и поведенческие истории.

Начать подготовку

Продолжить подготовку

Библиотека собеседований RecallDeck

Подробные русские ответы, разборы этапов найма и планы подготовки для российского IT-рынка.

RSS