Системный дизайн для собеседования

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

1. Фреймворк ответа: требования, ограничения, допущения

Фреймворк ответа: требования, ограничения, допущения

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

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

!Последовательность: вопрос → требования → ограничения → допущения → подтверждение → дизайн

Почему это важно именно на интервью

  • Снижает неопределённость: большинство задач сформулированы намеренно размыто.
  • Защищает от неверных ожиданий: вы не потратите 15 минут на решение не той проблемы.
  • Позволяет осознанно выбирать компромиссы: производительность, стоимость, консистентность, сложность.
  • Демонстрирует зрелость: умение фиксировать рамки и управлять рисками.
  • Термины: что есть что

    Собеседования часто путают эти понятия, поэтому проговаривайте их явно.

    | Понятие | Что это | Как звучит в речи | Типичные примеры | |---|---|---|---| | Требования | Что система должна делать и какие качества должна обеспечивать | "Нужно уметь…" / "Должны гарантировать…" | "Пользователь создаёт короткую ссылку", "Задержка до 200 мс на чтение" | | Ограничения | Что нельзя менять или чем вы связаны (технологии, правила, среда) | "Мы ограничены…" / "Нельзя…" | "Только один дата-центр", "Нельзя хранить PII", "Только Postgres" | | Допущения | То, что вы принимаете за правду, если данных нет, чтобы двигаться дальше | "Предположим, что…" | "1 млн DAU", "1000 RPS в пике", "Срок хранения 30 дней" |

    Полезная ментальная проверка:

  • Требования можно обсуждать и уточнять.
  • Ограничения обычно задаются извне.
  • Допущения вы выбираете, но обязаны проговорить и подтвердить.
  • Типы требований

    Функциональные требования

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

    Примеры формулировок:

  • "Пользователь может загрузить фото"
  • "Система генерирует короткий идентификатор"
  • "Должны быть права доступа на чтение и запись"
  • Нефункциональные требования

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

    Часто их называют NFR (non-functional requirements). Если хотите углубиться в термин, см. Non-functional requirement.

    Ключевые категории NFR, которые почти всегда уместно спросить:

  • Задержка (latency) на чтение и запись
  • Пропускная способность (throughput)
  • Доступность (availability)
  • Надёжность и восстановление (RTO/RPO — если уместно)
  • Консистентность данных (что важнее: свежесть или доступность)
  • Безопасность и приватность
  • Наблюдаемость (логи, метрики, алерты)
  • Стоимость (есть ли бюджетный потолок)
  • Важно: на интервью не нужно собирать все NFR. Выберите 3–5 самых влияющих на архитектуру и зафиксируйте их.

    Ограничения: что может “сломать” красивую архитектуру

    Ограничения — это рамки, которые могут запретить оптимальные на бумаге решения.

    Частые виды ограничений:

  • Платформа и инфраструктура: "только облако X", "on-prem"
  • Технологический стек: "у нас уже Kafka", "только SQL"
  • Регуляторика и приватность: "нельзя хранить IP", "данные только в регионе EU"
  • Организационные: "маленькая команда", "нельзя поддерживать 10 микросервисов"
  • Временные: "MVP за 2 месяца"
  • Правильный ход в ответе:

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

    Допущения нужны, потому что в задаче почти всегда нет чисел. Но допущения опасны, если они:

  • скрыты (интервьюер не понимает, почему вы выбрали именно это)
  • нереалистичны (например, занизили нагрузку в 100 раз)
  • не проверены (вы не предложили способ подтвердить их позже)
  • Хорошие допущения:

  • измеримы (числа, проценты, сроки)
  • привязаны к сценарию использования
  • явно подтверждены интервьюером
  • Мини-чеклист чисел, которые обычно стоит зафиксировать

  • Сколько пользователей: DAU/MAU, рост
  • Пиковая нагрузка: RPS, сезонность
  • Соотношение чтение/запись
  • Размер данных: средний объект, общий объём, прирост в день
  • География: один регион или несколько
  • SLO: целевые показатели (например, задержка, доступность)
  • Если интервьюер не даёт цифры, вы можете предложить диапазон и выбрать разумное значение:

  • "Если у нас 100 тыс. DAU, то…; если 10 млн DAU, то архитектура будет другой. Давайте возьмём 1 млн DAU как середину — ок?"
  • Фреймворк старта ответа (скрипт на 3–7 минут)

    Ниже — последовательность, которая почти всегда работает. Цель — быстро прийти к согласованным рамкам.

    Уточнить цель и границы

    Спросите:

  • Кто пользователь и какой главный сценарий?
  • Что является успехом (метрика, пользовательская ценность)?
  • Что не входит в задачу (админка, биллинг, рекомендации)?
  • Собрать функциональные требования

    Техника: начните с 3–5 основных пользовательских действий.

    Пример формата:

  • "Должны уметь: создать…, прочитать…, обновить…, удалить…, искать…"
  • Собрать 3–5 ключевых нефункциональных требований

    Обязательно привяжите NFR к сценарию:

  • "Для открытия страницы пользователем важна задержка чтения"
  • "Для финансовых операций важна консистентность"
  • Зафиксировать ограничения

    Спросите явно:

  • Есть ли заданный стек/облако/БД?
  • Есть ли требования по регионам и хранению данных?
  • Есть ли лимит по времени или команде?
  • Проговорить допущения и получить подтверждение

    Закрывающая фраза, которая дисциплинирует разговор:

  • "Я предположу X, Y, Z. Если ок — дальше оценю нагрузку и предложу архитектуру."
  • Если в требованиях вы используете модальные слова ("должно", "желательно"), полезно различать уровни строгости. В стандартах для требований часто используют словарь MUST/SHOULD/MAY — см. RFC 2119: Key words for use in RFCs to Indicate Requirement Levels.

    Пример: как это звучит на задаче “спроектируй сокращатель ссылок”

    Ниже — пример начала ответа, до архитектуры.

  • Уточнение цели: "Правильно ли понимаю, что основной сценарий — пользователь создаёт короткую ссылку и затем другие пользователи по ней переходят?"
  • Функциональные требования: "Нужно: создать короткую ссылку из длинной, редирект по короткой, опционально — кастомный алиас и срок жизни ссылки. Аналитика переходов входит?"
  • Нефункциональные требования: "Что важнее — низкая задержка редиректа или консистентность статистики? Какая целевая доступность?"
  • Ограничения: "Мы можем использовать любое облако/стек? Есть запрет на хранение исходных URL в открытом виде?"
  • Допущения (если нет ответов): "Предположим 1 млн активных пользователей в день, пиковая нагрузка 5 тыс. RPS на редирект, чтение/запись 100:1, срок хранения ссылок 1 год. Подходит?"
  • Обратите внимание: вы ещё не нарисовали ни одного сервиса, но уже создали рамки для выбора хранилища, кэша, партиционирования и стратегии доступности.

    Типичные ошибки и как их избегать

  • Ошибка: начинать с "возьмём Kafka и микросервисы". Исправление: сначала договориться о сценариях, нагрузке и SLO.
  • Ошибка: превращать уточнения в допрос на 10 минут. Исправление: собрать минимальный набор, остальное — допущениями.
  • Ошибка: не различать требования и допущения. Исправление: проговорить: "это требование" vs "это моё допущение".
  • Ошибка: игнорировать ограничения компании. Исправление: спросить про стек и регуляторику до выбора технологий.
  • Итоговый шаблон, который можно повторять на любой задаче

  • "Сначала уточню цель и что входит/не входит."
  • "Зафиксирую функциональные требования: …"
  • "Договорюсь о ключевых NFR: …"
  • "Проверю ограничения: …"
  • "Сделаю допущения по нагрузке и данным: … Подтверждаем?"
  • С этим шаблоном вы стабильно стартуете задачу, снижаете риск неверного направления и создаёте основу для следующего шага курса — оценки нагрузки и выбора базовой архитектуры.

    2. Оценка нагрузок: QPS, хранилище, пропускная способность

    Оценка нагрузок: QPS, хранилище, пропускная способность

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

  • прозрачными (понятно, откуда взялись)
  • согласованными (между собой и с допущениями)
  • достаточными, чтобы обосновать архитектурные решения
  • На интервью эта часть обычно занимает 5–10 минут и задаёт основу для выбора хранилищ, кэшей, шардирования, очередей и стратегии масштабирования.

    Что именно оцениваем

    Обычно достаточно трёх групп оценок:

  • QPS/RPS: сколько запросов в секунду система должна выдерживать (и отдельно чтение/запись)
  • Хранилище: сколько данных накапливается и с какой скоростью растёт
  • Пропускная способность (bandwidth): сколько байт в секунду проходит через границы системы (вход/выход) и через ключевые внутренние компоненты
  • В терминологии часто используют:

  • RPS (requests per second) и QPS (queries per second) как почти синонимы. На практике в системном дизайне под QPS часто подразумевают нагрузку на чтение (например, на БД), а под RPS — общий поток запросов к сервису. В ответе достаточно выбрать один термин и быть последовательным.
  • Минимальный алгоритм оценок на интервью

  • Зафиксировать, какие пользовательские действия создают нагрузку (основные эндпоинты).
  • Оценить активную аудиторию (DAU/MAU) и частоту действий.
  • Перевести действия в средний RPS и пиковый RPS.
  • Разложить нагрузку на чтение/запись и понять, куда она идёт (кэш, БД, очередь).
  • Прикинуть размер объектов и ретеншн (сколько храним и как долго).
  • Перевести размеры ответов/запросов в пропускную способность.
  • Если интервьюер не даёт чисел — используйте допущения (как в предыдущей статье), проговорите их и двигайтесь дальше.

    !Куда прикладывать оценки RPS/QPS, объёма данных и пропускной способности

    Оценка RPS/QPS

    Шаг 1: выделить операции

    Сначала назовите 3–6 главных операций, которые создают нагрузку, например:

  • чтение (открыть страницу, редирект, получить ленту)
  • запись (создать сущность, лайк, комментарий)
  • фоновые операции (пересчёт, доставка уведомлений)
  • Дальше для каждой операции оцените частоту.

    Шаг 2: средний RPS из DAU

    Базовая идея: сколько действий в день делят на количество секунд в дне.

    Формула для среднего RPS:

    Где:

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

    Шаг 3: пиковый RPS через коэффициент пика

    Часто используют коэффициент, который отражает неравномерность трафика (день/ночь, рабочие часы, всплески).

    Где:

  • — пиковый RPS
  • — коэффициент пика (часто берут 5–20 в зависимости от продукта)
  • Если нет данных, разумно сказать: «Возьму коэффициент пика 10, потому что у большинства consumer-сервисов сильная дневная концентрация».

    Шаг 4: разложить на чтение/запись

    Если известна пропорция чтение/запись (например, 100:1), вы можете перевести общий RPS в чтение и запись.

    Если соотношение чтение/запись равно , то доли такие:

    Где:

  • — общий RPS на endpoint или сервис
  • — RPS операций чтения
  • — RPS операций записи
  • и — числа из соотношения (например, , )
  • Эта арифметика полезна, потому что чтение часто уходит в кэш, а запись почти всегда упирается в хранилище или очередь.

    Короткий пример (в стиле интервью)

    Допустим, вы проектируете сокращатель ссылок.

  • Допущения:
  • -

  • в среднем 20 редиректов на пользователя в день
  • коэффициент пика
  • Средний RPS редиректа:
  • Пиковый RPS редиректа:
  • Дальше вы можете сказать: «Ок, редирект должен держать порядка 2–3k RPS в пике, значит кэш и горизонтальное масштабирование API точно понадобятся».

    Оценка хранилища

    Шаг 1: определить, что именно хранится

    Перечислите сущности и минимальный набор полей, который влияет на объём:

  • основная запись (например, short_code -> long_url)
  • метаданные (владелец, TTL, время создания)
  • индексы (часто сравнимы с данными по порядку величины)
  • логи/аналитика (если входит в требования)
  • На интервью достаточно оценки с запасом, но важно назвать, что вы включили, а что нет.

    Шаг 2: оценить размер записи

    Оценка размера записи — это приближённая сумма:

  • ключи и значения (строки, числа)
  • накладные расходы формата хранения и индексов
  • Если нет опыта с байтами, можно взять «разумные» порядки:

  • короткий ключ: 8–16 байт
  • URL: 100–500 байт (в среднем)
  • метаданные: 50–200 байт
  • индекс: 0.5–1.5 от размера данных (очень грубо, зависит от БД)
  • Главное — проговорить допущение.

    Шаг 3: рост данных и ретеншн

    Общий объём данных при постоянном притоке обычно считают так:

    Где:

  • — общий объём хранилища
  • — число новых записей в день
  • — срок хранения в днях (ретеншн)
  • — средний размер одной записи (в байтах)
  • — коэффициент репликации (например, 3 копии)
  • На интервью удобно считать в гигабайтах/терабайтах, но формула одинакова.

    Что часто забывают в оценке хранилища

  • Индексы и вторичные ключи увеличивают объём.
  • Репликация увеличивает объём линейно.
  • Хранение “навсегда” быстро превращает «маленькую» систему в большую.
  • Для time-series/логов важен не только объём, но и скорость записи.
  • Оценка пропускной способности (bandwidth)

    Пропускная способность важна, когда:

  • ответы большие (лента, изображения, видео)
  • много внутренних hops (fanout, микросервисы)
  • дорогой egress (выходящий трафик из облака)
  • Внешняя пропускная способность

    Для одного endpoint можно оценить:

    Где:

  • — пропускная способность в байтах в секунду
  • — запросы в секунду (RPS)
  • — средний размер полезной нагрузки (payload) в байтах
  • Если нужно учесть и запрос, и ответ, можно сложить их размеры: «запрос 1 КБ, ответ 20 КБ, итого 21 КБ на запрос».

    Внутренняя пропускная способность и fanout

    Опасный случай — когда один пользовательский запрос порождает много внутренних запросов.

    Если один входной запрос порождает внутренних операций, то внутренний RPS примерно:

    Где:

  • — входной RPS
  • — fanout (например, 100 запросов к storage/сервисам)
  • Так вы быстро объясняете, почему, например, «лента новостей с fanout на запись» может взорвать нагрузку на фоновые воркеры.

    Как презентовать оценки интервьюеру

    Хороший формат — короткая таблица, где видно, что именно нагружается.

    | Операция | Пиковый RPS | Тип нагрузки | Ключевой ресурс | |---|---:|---|---| | Редирект по короткой ссылке | 2.3k | чтение | кэш, затем KV/SQL | | Создание короткой ссылки | 50 | запись | БД (уникальность), генератор ID | | Логирование клика | 2.3k | запись | очередь, потоковая обработка |

    Числа выше примерные: на интервью важно не попасть «в точку», а показать, что архитектура следует из оценок.

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

  • Путать среднюю и пиковую нагрузку и проектировать под среднюю.
  • Считать только внешний RPS и не учитывать внутренний fanout.
  • Оценивать хранилище без ретеншна и репликации.
  • Давать точные числа без допущений: лучше приблизительно, но объяснимо.
  • Итоговый шаблон (что сказать вслух за 1–2 минуты)

  • «Я оценю нагрузку через DAU, действий на пользователя и коэффициент пика».
  • «Разделю чтение и запись, потому что они упираются в разные компоненты».
  • «Прикину объём данных через записи в день, ретеншн, размер записи и репликацию».
  • «Оценю bandwidth по размеру ответов и RPS, и отдельно проверю внутренний fanout».
  • Эти оценки станут входом для следующего шага системного дизайна: выбора архитектурных компонентов (кэш, БД, очередь), партиционирования и стратегии масштабирования.

    3. Высокоуровневая архитектура и основные компоненты

    Высокоуровневая архитектура и основные компоненты

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

    Цель этого этапа — не угадать «идеальный стек», а показать, что вы:

  • переводите требования в набор компонентов
  • понимаете, какие пути запросов критичны (read path vs write path)
  • закладываете масштабирование, надёжность и наблюдаемость
  • Что значит «высокоуровневая архитектура» на интервью

    Высокоуровневая архитектура — это:

  • список ключевых компонентов (обычно 6–12 блоков)
  • основные потоки: что происходит при чтении, что происходит при записи, что уходит в фон
  • где находятся «дорогие» места (БД, сеть, очереди) и как вы их защищаете (кэш, шардинг, асинхронность)
  • На этом уровне вы обычно не обсуждаете:

  • конкретные таблицы и индексы (это следующий слой детализации)
  • точные настройки таймаутов и thread pools
  • редкие оптимизации
  • Как перейти от требований и нагрузок к блок-схеме

    Практичный порядок действий (подходит почти для любой задачи):

  • Выберите 2–4 главных пользовательских сценария.
  • Для каждого сценария проговорите: какой это путь (чтение/запись), какой RPS, какая задержка важна.
  • Решите, что можно делать синхронно (в рамках ответа пользователю), а что лучше вынести в фон.
  • Нарисуйте компоненты «снаружи внутрь»: edge → API → бизнес-логика → хранилища/очереди.
  • Убедитесь, что каждый компонент имеет причину существования, связанную с требованиями.
  • !Типовая карта компонентов и два основных пути: чтение и запись

    Базовый набор компонентов и когда они нужны

    Ниже — «словарь» компонентов, которыми удобно оперировать на интервью. Если вы их называете, сразу добавляйте зачем они в вашей задаче.

    Edge-уровень: принять и распределить трафик

    DNS

    DNS направляет пользователя на нужный домен/адрес. В системном дизайне DNS часто всплывает в контексте:

  • гео-направления в регионы
  • failover (переключение при авариях)
  • CDN

    CDN (сеть доставки контента) кэширует статические и иногда динамические ответы ближе к пользователю.

    Используйте CDN, если:

  • ответы большие (картинки, видео)
  • география широкая, важна низкая задержка
  • есть повторяемые запросы
  • Справка: Content delivery network.

    Балансировщик нагрузки

    Балансировщик распределяет запросы по нескольким экземплярам сервиса.

    На интервью полезно проговорить 2 свойства:

  • горизонтальное масштабирование: добавили серверы, балансировщик «размазал» трафик
  • health checks: не отправляем запросы на «больные» инстансы
  • Справка: Load balancing).

    Вход в систему: API gateway и прикладной сервис

    API gateway (или reverse proxy)

    Это компонент, который стоит «перед» сервисами и решает общие задачи:

  • аутентификация и авторизация (кто ты и что тебе можно)
  • rate limiting (ограничение частоты запросов)
  • маршрутизация по сервисам
  • унификация логирования, трассировки
  • Важно: на маленькой системе gateway может быть логическим, а не отдельным продуктом. На интервью достаточно сказать: «эти функции должны где-то жить на входе».

    Справка: API gateway.

    Stateless application servers

    Это «основной сервис» с бизнес-логикой. Ключевая идея: держать его stateless (без состояния), чтобы:

  • легко масштабировать горизонтально
  • проще выкатывать и откатывать версии
  • Состояние (данные) лучше выносить в специализированные хранилища.

    Данные: кэш и хранилища

    Кэш

    Кэш — быстрый слой, который уменьшает нагрузку на БД и снижает задержку на чтение.

    Типовые вопросы, которые стоит проговорить:

  • что кэшируем (например, профиль, ленту, short_code -> long_url)
  • как инвалидируем (по TTL, по событиям, по записи)
  • что будет при промахе кэша (идём в БД)
  • Справка: Cache (computing)).

    Операционное хранилище (OLTP)

    Это основная база для транзакционных операций (создание/обновление сущностей, чтение по ключам).

    Чтобы не «застрять» в выборе конкретной БД на раннем этапе, удобно говорить языком паттернов доступа:

  • чтение по ключу (часто подходит key-value или хорошо индексированная реляционная БД)
  • запросы по нескольким полям и связи (часто удобна реляционная модель)
  • гибкая схема и документы (иногда уместна документная модель)
  • На высоком уровне достаточно зафиксировать:

  • нужна ли сильная консистентность для записей
  • как будет масштабироваться чтение (реплики) и запись (шардинг, если нужно)
  • Справка: Online transaction processing.

    Объектное хранилище

    Если в задаче есть файлы (фото, видео, вложения), обычно выносите их в объектное хранилище, а в БД храните ссылки и метаданные.

    Справка: Object storage.

    Асинхронность: очереди, стримы и воркеры

    Очередь сообщений / стриминг

    Очередь помогает вынести работу из критичного пути пользователя:

  • отправка уведомлений
  • запись аналитики
  • обработка медиа
  • fanout (массовая доставка событий подписчикам)
  • Важно проговорить отличие на уровне идеи:

  • очередь часто про «доставить задачу воркеру»
  • стрим часто про «поток событий, который читают несколько потребителей»
  • Справка: Message queue.

    Фоновые воркеры

    Воркеры читают задачи из очереди и выполняют «тяжёлую» работу. На интервью полезно упомянуть:

  • ретраи (повторы при временных сбоях)
  • dead-letter queue (куда попадают «неподъёмные» сообщения)
  • идемпотентность (повтор не должен ломать данные)
  • Справка: Idempotence.

    Дополнительные компоненты, которые часто нужны

    Поиск

    Если в требованиях есть поиск по тексту или сложная фильтрация, часто выделяют отдельный поисковый индекс.

    На высоком уровне достаточно описать идею:

  • основной источник правды — БД
  • индекс строится асинхронно (события/батчи)
  • Аналитическое хранилище (OLAP)

    Для отчётов и аналитики (агрегации по большим объёмам) обычно отделяют контур от OLTP, чтобы не «убить» основную БД тяжёлыми запросами.

    Справка: Online analytical processing.

    Наблюдаемость

    На интервью это часто «бесплатные» баллы зрелости. Минимальный набор:

  • логи (что случилось)
  • метрики (сколько и как быстро)
  • трассировка (где задержка на цепочке вызовов)
  • Справка: Observability.

    Типовые архитектурные решения, которые должны следовать из чисел

    Разделяйте read path и write path

    Если из оценок видно соотношение чтение/запись вроде 100:1, обычно логично:

  • ускорять чтение кэшем, CDN, репликами
  • защищать запись очередями, батчингом, аккуратной моделью данных
  • Выносите «необязательное» из ответа пользователю

    Хороший вопрос к каждому шагу в синхронном пути:

  • это нужно, чтобы пользователь прямо сейчас получил ответ?
  • Если нет — кандидат на асинхронность.

    Сначала простая масштабируемость, потом сложная

    На высоком уровне обычно достаточно:

  • stateless сервисы + балансировщик
  • кэш на горячих чтениях
  • реплики на чтение
  • Шардинг, мульти-регион, сложные протоколы консистентности стоит вводить только если ваши оценки показывают необходимость или есть жёсткие требования.

    Как презентовать high-level дизайн на собеседовании

    Удобный «скрипт» на 60–120 секунд:

  • «У нас есть 2 ключевых сценария: чтение X и запись Y. Чтение критично по задержке, запись критична по корректности».
  • «На входе CDN/балансировщик/шлюз: терминируем TLS, делаем rate limit и аутентификацию».
  • «Основной сервис stateless, масштабируется горизонтально».
  • «Для чтения: кэш, затем БД (и реплики, если чтения много)».
  • «Для фоновых задач: очередь и воркеры (например, аналитика, уведомления)».
  • «Везде метрики/логи/трассировка, чтобы видеть задержки и ошибки».
  • Пример: сокращатель ссылок (high-level)

    Предположим, на предыдущем шаге вы оценили, что редиректы — основная нагрузка, и они сильно read-heavy.

    Путь редиректа (чтение)

  • клиент запрашивает GET /{short_code}
  • API ищет short_code в кэше
  • при промахе идёт в хранилище short_code -> long_url, кладёт в кэш, возвращает редирект
  • событие клика отправляет асинхронно в очередь (чтобы не тормозить редирект)
  • Путь создания ссылки (запись)

  • клиент отправляет POST /links
  • сервис генерирует новый идентификатор (или проверяет кастомный алиас)
  • записывает mapping в БД
  • возвращает короткую ссылку
  • !Пример high-level дизайна сокращателя ссылок с кэшем и асинхронной аналитикой

    Частые ошибки на этом этапе

  • Перечислять технологии без привязки к требованиям: «возьмём Kafka, потому что модно» вместо «у нас 2k RPS кликов, логирование лучше вынести в асинхронный контур».
  • Рисовать только компоненты и забывать потоки: интервьюеру важно видеть как проходит запрос.
  • Не выделять критичный путь: на большинстве задач важно показать, что вы оптимизируете именно то, что влияет на SLO.
  • Смешивать операционную нагрузку и аналитику в одном контуре без причины.
  • Итог

    Высокоуровневая архитектура на интервью — это связка из трёх вещей:

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

    4. Хранилища, кэширование, консистентность и репликация

    Хранилища, кэширование, консистентность и репликация

    На прошлых шагах вы:

  • договорились о требованиях, ограничениях и допущениях
  • оценили нагрузки (RPS/QPS, объёмы, bandwidth)
  • набросали высокоуровневую архитектуру и потоки (read path и write path)
  • Теперь нужно сделать дизайн практичным: выбрать типы хранилищ и кэшей, решить, какой уровень консистентности нужен, и как репликация повлияет на задержку, доступность и сложность.

    На собеседовании этот блок обычно отличает «я знаю набор технологий» от «я понимаю компромиссы и могу объяснить выбор».

    !Типовая схема: кэш + primary + реплики и разделение путей чтения/записи

    Как выбирать хранилище на интервью

    Полезный принцип: не начинайте с названия БД, начните с паттерна доступа и требований.

  • Ключевой доступ по ключу: id -> объект и очень много чтений.
  • Запросы по нескольким полям, сортировки, транзакции: нужны индексы, связи, ограничения.
  • Большие бинарные объекты: фото/видео/архивы.
  • Время-ориентированные события и аналитика: поток событий, агрегации, отчёты.
  • Дальше вы сопоставляете это с NFR из первого шага:

  • требуется ли строгая корректность (например, баланс счёта)
  • насколько критична задержка чтения
  • насколько критична доступность при сбоях
  • какие объёмы и скорость записи
  • Справочно по терминам полезны:

  • ACID
  • NoSQL
  • Object storage
  • Основные типы хранилищ и когда они уместны

    Реляционная OLTP БД

    Подходит, если нужны:

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

    Справка: Online transaction processing

    Key-value хранилище

    Подходит, если основная операция:

  • быстрое чтение/запись по ключу
  • Типичный пример в интервью: сокращатель ссылок (short_code -> long_url) или хранение сессий.

    Справка: Key%E2%80%93value database

    Документная БД

    Уместна, если:

  • схема данных меняется часто
  • объект естественно хранить целиком документом
  • запросы в основном по полям одного документа
  • Справка: Document-oriented database

    Колоночные и аналитические хранилища

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

  • OLTP контур обслуживает продукт
  • OLAP контур обслуживает аналитику
  • Справка: Online analytical processing

    Объектное хранилище

    Почти всегда лучше для файлов, чем хранить их в OLTP базе:

  • в БД лежит метадата и ссылки
  • сами файлы лежат в объектном хранилище
  • Справка: Object storage

    Кэширование: зачем, где и как

    Кэш почти всегда появляется, когда по вашим оценкам нагрузок:

  • чтение сильно доминирует над записью
  • нужно уменьшить задержку чтения
  • нужно защитить базу от пиков
  • Справка: Cache (computing))

    Что кэшировать

    На интервью перечислите 2–4 конкретных объекта:

  • результат чтения по ключу: профиль пользователя, short_code -> long_url
  • «кусок» ленты или списка (если допускается устаревание)
  • токены/сессии (если это входит в задачу)
  • Плохой стиль: «закэшируем всё» без объяснения инвалидации и источника правды.

    Где кэшировать

    Обычно есть несколько слоёв:

  • CDN для статики и кэшируемого контента на edge
  • кэш рядом с приложением (например, Redis/Memcached)
  • локальный кэш в процессе (осторожно с консистентностью и прогревом)
  • Справка:

  • Content delivery network
  • Redis
  • Memcached
  • Как кэш уменьшает QPS на базу

    Если входной QPS равен , а доля попаданий в кэш равна (cache hit rate), то ожидаемый QPS на базу:

    Где:

  • это общий поток запросов на чтение
  • это доля запросов, обслуженных кэшем, от 0 до 1
  • это остаток запросов, который идёт в БД при промахе кэша
  • Эта формула на интервью полезна не точностью, а тем, что вы показываете связь между метрикой кэша и нагрузкой на storage.

    Стратегии записи и чтения с кэшем

    Самые частые паттерны:

  • Cache-aside: приложение читает из кэша, при промахе идёт в БД и затем кладёт в кэш.
  • Read-through: слой кэша сам ходит в БД при промахе.
  • Write-through: запись проходит через кэш и синхронно попадает в БД.
  • Write-back: запись сначала в кэш, а в БД позже асинхронно.
  • Для интервью обычно достаточно:

  • cache-aside для большинства read-heavy задач
  • write-through, если критично, чтобы кэш не расходился с БД
  • write-back только если вы готовы обсуждать риск потери данных и сложность восстановления
  • Инвалидация и TTL

    Кэширование сложно не чтением, а тем, как вы поддерживаете актуальность.

    Распространённые подходы:

  • TTL: данные протухают через фиксированное время
  • инвалидация по событию: после записи в БД удаляем ключ из кэша
  • версия/etag: храним вместе с версией и проверяем свежесть
  • В интервью важно проговорить:

  • что будет при устаревании (какая цена ошибки)
  • какой допустимый лаг между БД и кэшем
  • Типичные проблемы кэша и как о них говорить

    Cache stampede

    Когда популярный ключ истёк, много запросов одновременно идут в БД.

    Митигировать можно так:

  • lock на ключ на время пересчёта
  • jitter к TTL (случайная добавка)
  • stale-while-revalidate: отдаём слегка устаревшее значение, а обновление делаем в фоне
  • Справка: Cache stampede

    Hot keys

    Один ключ получает огромную долю трафика.

    Типовые меры:

  • репликация кэша и корректное шардирование
  • локальный in-process кэш на короткий TTL
  • ограничение частоты запросов на входе
  • Cache penetration

    Запросы по несуществующим ключам бьют в БД.

    Частые решения:

  • негативное кэширование: кэшировать факт отсутствия на короткий TTL
  • фильтры вероятности, например Bloom filter
  • Консистентность: что это и как выбрать уровень

    Консистентность в интервью часто сводится к вопросу: допускаем ли мы, что разные пользователи увидят разные версии данных в течение некоторого времени?

    Полезные модели:

  • Strong consistency: после успешной записи все последующие чтения видят запись.
  • Eventual consistency: система со временем сходится, но некоторое время возможны устаревшие чтения.
  • Справка:

  • Consistency model
  • CAP theorem
  • Как связать консистентность с требованиями

    На интервью лучше всего работают конкретные примеры:

  • деньги, права доступа, уникальные ограничения: обычно нужна более строгая консистентность
  • лента, счётчик просмотров, рекомендации: часто достаточно eventual consistency
  • Важно проговорить границу, где вы обеспечиваете строгость:

  • «источник правды это primary БД, записи там строго консистентны»
  • «кэш и реплики могут отдавать слегка устаревшие данные, если это приемлемо по продукту»
  • !Иллюстрация репликационного лага и устаревших чтений

    Репликация: зачем нужна и какие компромиссы

    Репликация это хранение копий данных на нескольких узлах. В интервью она обычно нужна для двух целей:

  • масштабирование чтения: read replicas берут на себя часть read QPS
  • надёжность: пережить падение узла и быстрее восстановиться
  • Справка: Replication (computing))

    Primary-replica (leader-follower)

    Самый частый шаблон:

  • все записи идут в leader
  • follower получает изменения и обслуживает часть чтений
  • Компромиссы:

  • проще обеспечить корректность записей
  • возможен replication lag, значит чтения с реплик могут быть устаревшими
  • Справка: Leader%E2%80%93follower replication

    Синхронная и асинхронная репликация

  • синхронная: запись подтверждается после записи в несколько узлов
  • асинхронная: leader подтверждает запись сразу, а реплики догоняют позже
  • Как это объяснять на интервью:

  • синхронная повышает консистентность, но увеличивает задержку записи
  • асинхронная снижает задержку записи, но допускает лаг и риск потери последних записей при аварии лидера
  • Failover и «кто становится лидером»

    Если лидер падает, система должна выбрать нового лидера.

    На высоком уровне достаточно проговорить:

  • есть механизм обнаружения сбоя (health checks/timeout)
  • есть процесс выбора лидера (вручную или автоматически)
  • после переключения важно не допустить split brain, когда два лидера принимают записи
  • Если интервьюер углубляется, можно упомянуть, что выбор лидера часто требует координации и консенсуса, но не уходить в детали алгоритмов без запроса.

    Справка: Split-brain (computing))

    Частые связки решений (как «собрать» ответ на интервью)

    Read-heavy сервис с низкой задержкой чтения

  • cache-aside (Redis) на горячих ключах
  • read replicas для снижения нагрузки на primary
  • допускаем eventual consistency на чтении (если продукт позволяет)
  • Write-critical сервис (корректность важнее задержки)

  • записи строго в primary с транзакциями
  • кэш либо только на чтение, либо с аккуратной инвалидацией
  • чтения критичных данных с primary или с реплик при гарантиях, что лаг допустим
  • Большие файлы

  • объектное хранилище для контента
  • CDN для доставки
  • метадата в OLTP
  • Как презентовать этот блок интервьюеру

    Удобный формат из 6 фраз:

  • «Источник правды это X (обычно primary OLTP).»
  • «Чтения ускоряем кэшем; при промахе идём в БД и прогреваем кэш.»
  • «Инвалидация: TTL и удаление ключа после записи в БД.»
  • «Чтения масштабируем репликами, но учитываем replication lag.»
  • «Для критичных чтений используем primary, для некритичных можно реплики.»
  • «Компромисс: меньше задержка и выше доступность, но возможны устаревшие данные на чтении.»
  • Итог

    Хранилища, кэширование, консистентность и репликация на собеседовании оцениваются не по тому, знаете ли вы названия систем, а по тому, умеете ли вы:

  • вывести выбор storage из паттернов доступа и NFR
  • объяснить стратегию кэша и инвалидации
  • честно проговорить, где допускается eventual consistency
  • показать, как репликация влияет на чтение, запись, задержки и отказоустойчивость
  • Это делает ваш high-level дизайн из предыдущей статьи реалистичным и готовым к детализации в следующих шагах: модели данных, партиционирование и отказоустойчивые сценарии.

    5. Надёжность, масштабирование, мониторинг и обсуждение компромиссов

    Надёжность, масштабирование, мониторинг и обсуждение компромиссов

    В предыдущих статьях курса вы научились:

  • фиксировать требования, ограничения и допущения
  • оценивать нагрузку (RPS/QPS, хранилище, bandwidth)
  • собирать high-level архитектуру
  • выбирать хранилища и кэш, понимать консистентность и репликацию
  • На собеседовании следующий слой зрелости — показать, что вы проектируете систему, которая живёт в реальном мире: ломается, деградирует, испытывает пики трафика, требует наблюдаемости и требует принятия компромиссов.

    Эта статья даёт практичный набор “якорей”, которые можно проговаривать почти в любой задаче: как обеспечить надёжность, как масштабироваться, как мониторить и как честно обсуждать компромиссы.

    !Карта типовых механизмов надёжности по слоям архитектуры

    Надёжность: о чём именно речь

    На интервью “надёжность” часто путают с “высокой доступностью”. На практике это несколько разных свойств:

  • Доступность (availability): система отвечает на запросы.
  • Корректность (correctness): ответы правильные с точки зрения бизнес-логики.
  • Долговечность (durability): данные не теряются после подтверждённой записи.
  • Устойчивость (resilience): система выдерживает частичные сбои и перегрузку, деградируя предсказуемо.
  • Чтобы говорить предметно, в индустрии используют связку SLI/SLO/SLA:

  • SLI (service level indicator) — измеримый индикатор, например задержка p95 или доля успешных запросов.
  • SLO (service level objective) — целевое значение SLI, например “99.9% запросов успешны за 30 дней”.
  • SLA (service level agreement) — договор с пользователем/клиентом, часто с последствиями за нарушение.
  • Ссылки для терминов:

  • Service level indicator
  • Service-level objective
  • Service-level agreement
  • Error budget как язык компромиссов

    Практичная идея для интервью: если есть SLO, то есть и бюджет ошибок.

    Пример: при SLO 99.9% за 30 дней допустимо 0.1% “плохого” времени или запросов. Этот бюджет можно “тратить” на релизы, миграции и эксперименты. Это удобная рамка, чтобы объяснять, почему иногда выбирают более простое решение, а не максимальную надёжность любой ценой.

    Дизайн под сбои: что ломается и как это закрывать

    На собеседовании не требуется перечислять всё подряд. Лучше взять 3–6 наиболее вероятных и дорогих сценариев и показать, что архитектура их учитывает.

    Типовые классы сбоев

  • Перегрузка (пики RPS, горячие ключи, рост fanout)
  • Частичные падения (один сервис или одна зона недоступны)
  • Медленные зависимости (БД “жива”, но отвечает секунды)
  • Сетевые проблемы (потери пакетов, временные разрывы)
  • Ошибки данных (дубликаты событий, “повторная доставка” из очереди)
  • Таймауты, ретраи и идемпотентность

    Если вы называете ретраи, почти всегда нужно сказать и про ограничения.

  • Таймауты
  • 1. Каждый сетевой вызов должен иметь таймаут, иначе зависшие запросы съедят пул потоков и система “умрёт” каскадно. 2. Таймаут подбирают так, чтобы он был меньше клиентского и учитывал бюджет задержки всего запроса.
  • Ретраи
  • 1. Ретраи уместны для временных сбоев (например, 503, сетевой таймаут), но опасны при перегрузке. 2. Нужны exponential backoff и случайная добавка (jitter), иначе вы синхронно добьёте зависимость.
  • Идемпотентность
  • 1. Если запрос можно повторить, важно, чтобы повтор не создавал дубликаты (например, “создать заказ”). 2. Типовой приём: идемпотентный ключ (например, Idempotency-Key) + хранение результата операции на время.

    Термины:

  • Exponential backoff
  • Idempotence
  • Circuit breaker и bulkhead

    Это механизмы, которые предотвращают “эффект домино”, когда одна плохая зависимость кладёт весь сервис.

  • Circuit breaker
  • 1. Если зависимость часто падает или тормозит, мы временно “размыкаем цепь” и быстро возвращаем ошибку или деградацию, не занимая ресурсы. 2. Это даёт системе шанс восстановиться и защищает остальные зависимости.
  • Bulkhead
  • 1. Разделяем ресурсы по “отсекам”: разные пулы потоков/лимиты для разных типов запросов. 2. Тогда тяжёлые запросы не съедят ресурсы, нужные для критичного пути.

    Термины:

  • Circuit breaker
  • Rate limiting, backpressure и load shedding

    Эти механизмы отвечают на вопрос: что мы делаем, когда трафика больше, чем мы можем переварить?

  • Rate limiting
  • 1. Ограничиваем частоту запросов (на пользователя, токен, IP, ключ API), чтобы защитить систему и соседей. 2. На интервью полезно уточнить: где лимитируем (edge, gateway, сервис) и что возвращаем (например, 429).
  • Backpressure
  • 1. Сигнализируем upstream-компоненту “мне плохо”, чтобы он снижал скорость. 2. Часто проявляется как ограничение очереди, ограничение параллелизма, отказ от части задач.
  • Load shedding
  • 1. Осознанно отбрасываем некритичные запросы или функциональность, чтобы сохранить критичный путь. 2. Пример: временно отключить “рекомендации” или “детальную аналитику”, но сохранить “покупку”.

    Термин:

  • Rate limiting
  • Graceful degradation

    На собеседовании сильный сигнал зрелости — вы можете сказать, как система будет деградировать.

    Примеры деградации, которые часто уместны:

  • отдаём данные из кэша даже если они слегка устарели
  • отключаем тяжёлые вторичные фичи (поиск, рекомендации), сохраняя базовые операции
  • показываем “последнее известное состояние” вместо ошибки, если это приемлемо продуктово
  • Здесь важно связать деградацию с NFR из первой статьи: где допустимы устаревшие данные, а где нельзя.

    Масштабирование: как расти без переписывания всего

    Масштабирование на интервью — это не только “добавим серверов”. Вы должны показать, какой компонент станет узким местом и каким способом вы снимаете нагрузку именно с него.

    Горизонтальное масштабирование stateless-сервисов

    Если в high-level дизайне у вас есть stateless application servers за балансировщиком, вы уже заложили базовую масштабируемость:

  • добавляем инстансы
  • балансировщик распределяет запросы
  • состояние хранится во внешних системах (БД, кэш, очередь)
  • Термин:

  • Horizontal scaling
  • Масштабирование чтения

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

  • кэш (уменьшает QPS на БД)
  • CDN (если есть кэшируемый контент)
  • read replicas (масштабируют чтение ценой возможного replication lag)
  • Важно проговорить: какие чтения можно отдавать с реплик и кэша, а какие должны идти в primary (см. прошлую статью про консистентность и репликацию).

    Масштабирование записи

    Записи сложнее: чаще требуют уникальности, транзакций, упираются в один primary или в координацию.

    Типовые приёмы:

  • Асинхронность через очередь
  • 1. Если часть работы не нужна пользователю прямо сейчас (аналитика, уведомления, пересчёты), выносите её в очередь. 2. Это сглаживает пики и даёт backpressure естественным образом через длину очереди.
  • Шардирование (когда действительно нужно)
  • 1. Если primary БД упирается в лимит записи или объём, данные делят на шарды по ключу. 2. На интервью достаточно назвать принцип (например, “по user_id”), а также упомянуть цену: сложнее запросы, миграции, баланс.

    Термин:

  • Database sharding)
  • Автомасштабирование и планирование ёмкости

    Интервьюеры любят практику:

  • что именно вы будете масштабировать автоматически
  • по каким метрикам
  • какие ограничения у автоскейла
  • Примеры сигналов для автоскейла:

  • загрузка CPU/памяти у stateless сервисов
  • p95 latency на обработке запросов
  • длина очереди и возраст сообщений (lag)
  • hit rate кэша (падение hit rate может взорвать БД)
  • И важно сказать одну фразу про пределы:

  • автоскейл не спасёт, если упёрлись в “центральную” зависимость (один primary, лимит соединений к БД, лимит на запись диска)
  • !Сводная карта масштабирования по компонентам

    Мониторинг и наблюдаемость: как понять, что система здорова

    Если надёжность — это “как мы строим”, то наблюдаемость — это “как мы понимаем, что происходит”. Три базовых столпа:

  • метрики: численные временные ряды (latency, RPS, error rate)
  • логи: события и контекст
  • трейсы: цепочки вызовов между сервисами
  • Термин:

  • Observability
  • Практичные фреймворки метрик: RED и USE

    На интервью удобно упомянуть один из подходов.

  • RED для сервисов: Rate, Errors, Duration
  • USE для ресурсов: Utilization, Saturation, Errors
  • Ссылки:

  • The RED Method
  • The USE Method
  • Минимальный набор, который стоит проговорить

    | Зона | Что мониторим | Зачем | |---|---|---| | Edge / gateway | RPS, 4xx/5xx, rate limit events | видеть атаки, ошибки клиента, перегрузку | | API сервис | p50/p95/p99 latency, error rate, saturation пулов | ловить деградацию критичного пути | | Кэш | hit rate, latency, evictions, hot keys | понимать, когда кэш перестаёт защищать БД | | БД | latency, connections, replication lag, disk/IO | заранее увидеть “бутылочное горлышко” | | Очереди/стримы | lag, возраст сообщений, DLQ, retry rate | контролировать асинхронный контур |

    Алерты: что важно, чтобы не “утонуть”

  • Алерты должны быть по симптомам, а не по причинам
  • 1. Симптом: рост 5xx, рост p95 latency, рост lag. 2. Причина выясняется уже в расследовании.
  • Различайте “страница” и “тикет”
  • 1. Page: пользователь страдает прямо сейчас (например, SLO нарушается). 2. Ticket: деградация не критичная, но требует внимания.
  • Корреляция через идентификаторы
  • 1. request_id или trace_id в логах всех сервисов ускоряет поиск причин.

    Как обсуждать компромиссы на собеседовании

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

  • назвать варианты
  • выбрать один осознанно
  • объяснить цену выбора
  • предложить план эволюции
  • Универсальный шаблон фразы

  • “В этом месте есть компромисс между A и B.”
  • “Для наших требований важнее A, поэтому выбираю решение X.”
  • “Цена: негативное последствие Y.”
  • “Митигируем так: меры Z.”
  • “Если вырастем до условия W, перейдём на план эволюции.”
  • Частые компромиссы, которые уместно проговаривать

  • Консистентность vs доступность: чтение с реплик или кэша может быть устаревшим, но доступность и latency лучше. Справка: CAP theorem.
  • Задержка записи vs надёжность записи: синхронная репликация повышает уверенность, но увеличивает latency.
  • Стоимость vs производительность: больше кэша и реплик обычно дороже.
  • Сложность vs скорость разработки: микросервисы, мульти-регион и сложные протоколы повышают операционную стоимость.
  • Примеры “интервью-объяснений”

  • Read-heavy система
  • 1. Выбор: “кэш + read replicas”. 2. Цена: “возможны устаревшие чтения из реплик и кэша”. 3. Митигируем: “критичные чтения — из primary, TTL/инвалидация, мониторим replication lag”.
  • Пики трафика
  • 1. Выбор: “rate limiting + load shedding для некритичных эндпоинтов”. 2. Цена: “часть пользователей получит 429 или урезанную функциональность”. 3. Митигируем: “приоритизация критичных операций, отдельные лимиты, информирование клиента”.
  • Асинхронные процессы
  • 1. Выбор: “очередь и воркеры”. 2. Цена: “eventual consistency и задержка доставки”. 3. Митигируем: “идемпотентные обработчики, DLQ, мониторинг lag, ретраи с backoff”.

    Как завершать ответ на интервью

    Короткий финальный “слой зрелости” после high-level архитектуры обычно звучит так:

  • “Критичный путь чтения/записи защищаем кэшем, таймаутами и ограничениями.”
  • “Чтение масштабируем кэшем и репликами, запись защищаем очередями и контролем перегрузки.”
  • “Проектируем деградацию: что отключаем первым и что обязаны сохранить.”
  • “Закладываем наблюдаемость: метрики RED/USE, логи с request_id, трассировку, алерты по SLO.”
  • “Явно проговариваем компромиссы и план роста.”
  • Если вы стабильно добавляете этот слой в конце, ваш дизайн выглядит не как “картинка”, а как система, которую можно эксплуатировать.