Кратко
Структура интервью
Как структурировать интервью по System Design так, чтобы упорядочить мысли и сфокусироваться на самых важных аспектах
Самый простой способ саботировать свои шансы получить оффер в интервью по проектированию систем - не довести до конца работающее решение. Это самая частая причина, по которой кандидаты уровня middle проваливают такие интервью, и обычно это скрывается под туманной формулировкой "тайм-менеджмент". Проблема не в том, что вам нужно работать в два раза быстрее - часто достаточно просто сосредоточиться на правильных вещах.
Наша структура построения решения на System Design интервью - это последовательность шагов и рекомендованного распределения времени по ним. Если структурировать интервью таким образом, вы будете сфокусированы на том, что наиболее важно для интервьюера. Многие кандидаты (вполне объяснимо!) нервничают на интервью. Очень легко потеряться, если вы не наращиваете решение шаг за шагом.
Хотя четкая структура подхода важна, интервьюер обычно не оценивает сам подход (часто эта часть оценки попадает в корзину "коммуникация"). Но, на практике, многие кандидаты заметно лучше показывают себя, когда следуют структуре, которая не дает им застрять в какой-то конкретной части и помогает довести решение до работающего состояния.
Вот сама структура:
Требования (~5 минут)
Цель этапа "Требования" - получить ясное понимание системы, которую вас просят спроектировать. Для этого мы предлагаем разделить требования на две части.
Функциональные требования
Функциональные требования - это формулировки в стиле "Пользователи/клиенты могут...". Это ключевые фичи вашей системы, и именно их стоит обсуждать первыми с интервьюером. Задавайте точные вопросы так, как если бы вы общались с клиентом, заказчиком или продакт-менеджером ("нужна ли системе возможность X?", "что должно происходить, если Y?"), чтобы прийти к приоритизированному списку основных возможностей.
Например, если бы вы проектировали систему вроде Twitter, у вас могли бы получиться такие функциональные требования:
- Пользователи могут публиковать твиты
- Пользователи могут подписываться на других пользователей
- Пользователи могут видеть твиты тех, на кого они подписаны
А у кэша, например, функциональные требования могли бы быть такими:
- Клиенты могут вставлять элементы
- Клиенты могут задавать срок жизни (expiration) элементов
- Клиенты могут читать элементы
Держите требования сфокусированными! Главная цель оставшейся части интервью - разработать систему, которая удовлетворяет выбранным требованиям, поэтому важно стратегически подойти к приоритизации. У многих систем сотни фич, при этом ваша задача - выделить и приоритизировать топ‑3. Длинный список требований скорее навредит, чем поможет, и многие топовые компании напрямую оценивают вашу способность фокусироваться на главном.
Нефункциональные требования
Нефункциональные требования - это утверждения о свойствах системы, которые важны пользователям. Их можно формулировать как "Система должна уметь..." или "Система должна быть...".
Например, если бы вы проектировали систему вроде Twitter, у вас могли бы быть такие нефункциональные требования:
- Система должна быть высокодоступной, ставя доступность выше согласованности
- Система должна масштабироваться для поддержки 100M+ DAU
- Система должна обеспечивать низкую задержку, формируя ленту менее чем за 200 мс
Важно, чтобы нефункциональные требования были привязаны к контексту системы и, по возможности, выражены количественно. Например, "система должна обеспечивать низкую задержку" очевидно и не очень содержательно - почти все системы должны быть такими. Гораздо полезнее сформулировать так: "время поиска в системе должно быть менее 500 мс" - это указывает, какая часть системы должна быть особенно быстрой, и задает целевое значение.
Сформулировать нефункциональные требования бывает сложно, особенно если вы не очень знакомы с предметной областью. Ниже - список аспектов, которые могут помочь выделить самые важные нефункциональные требования для вашей системы. Обычно достаточно выбрать 3–5 наиболее релевантных.
- Теорема CAP: должна ли система приоритизировать согласованность или доступность (устойчивость к разделению сети, partition tolerance, в распределенных системах считается данностью)?
- Ограничения среды: есть ли ограничения среды, в которой будет работать система? Например, мобильное устройство с ограниченной батареей. Или устройства с ограниченной памятью/пропускной способностью (например, стриминг видео по 3G).
- Масштабируемость: все системы должны масштабироваться, но есть ли у этой системы особые требования к масштабированию? Например, всплески трафика в определенное время суток. Или события, вроде праздников, которые сильно повышают нагрузку. Также оцените соотношение чтения и записи: что важнее масштабировать - чтение или запись?
- Время ответа: как быстро система должна отвечать на запросы пользователей? Особенно учитывайте запросы, где есть значительные вычисления.
- Надежность хранения (durability): насколько важно, чтобы данные не терялись? Например, социальная сеть может пережить некоторую потерю данных, а банковская система - нет.
- Безопасность: насколько защищенной должна быть система? Учитывайте защиту данных, контроль доступа и соответствие требованиям регулирования.
- Отказоустойчивость: насколько хорошо система должна справляться со сбоями? Учитывайте избыточность, аварийное переключение и восстановление.
- Регуляторные требования: есть ли юридические или регуляторные требования? Учитывайте отраслевые стандарты, законы о защите данных и т.п.
Оценки "на салфетке"
Во многих руководствах предлагают на этом этапе сделать оценки "на салфетке" (back-of-the-envelope estimations). Мы считаем, что это часто не нужно. Вместо этого делайте расчеты только тогда, когда они напрямую влияют на дизайн. В большинстве случаев вы проектируете большую распределенную систему - и разумно исходить из этого. Многие кандидаты тратят 5 минут на то, чтобы посчитать объемы хранения, DAU и QPS, а затем приходят к выводу: "Окей, данных много, QPS высокие, все понятно". Интервьюеры не получают из этого ничего, кроме подтверждения, что вы умеете складывать и умножать числа.
Мы предлагаем сказать интервьюеру, что вы хотели бы пропустить оценки на старте и делать вычисления по ходу проектирования - когда/если это станет необходимо. Когда это необходимо? Представьте, что вы проектируете TopK‑систему для трендовых тем на YouTube. Вам важно оценить, сколько уникальных тем вы ожидаете увидеть - это определит, можно ли использовать один экземпляр структуры данных вроде min‑heap, или придется шардировать ее между несколькими экземплярами, что сильно повлияет на дизайн.
Некоторые интервьюеры могут попросить вас сделать приблизительные оценки даже после вашего обоснования. В этом случае, сделайте их сразу, чтобы не тратить время на обсуждение уместности и ценности этих оценок.
Как бы вы ни использовали это на интервью, умение быстро оценивать релевантные величины поможет вам лучше и точнее рассуждать о компромиссах в вашем дизайне.
API и сущности (~5 минут)
Прежде чем переходить к высокоуровневому проектированию, стоит определить "контракт" между системой и ее пользователями. Часто (но не всегда!) он напрямую соответствует функциональным требованиям, которые вы уже определили. Вы будете использовать этот контракт при создании high-level дизайна и проверять, что вы действительно удовлетворяете требованиям к системе.
Вначале нужно принять решение о том, какой протокол API использовать.
REST (Representational State Transfer): использует HTTP‑глаголы (GET, POST, PUT, DELETE) для CRUD‑операций над ресурсами. Это ваш выбор по умолчанию для большинства интервью.
GraphQL: позволяет клиентам запрашивать ровно те данные, которые им нужны, избегая избыточной (over‑fetching) и недостаточной (under‑fetching) выборки. Выбирайте, когда у вас есть разные клиенты с разными потребностями по данным.
RPC (Remote Procedure Call): протокол, ориентированный на действия (например, gRPC), который быстрее REST для взаимодействия между сервисами. Используйте для внутренних API, когда критична производительность.
Не переусложняйте. По умолчанию выбирайте REST, если нет явной причины делать иначе. Для real‑time фич вам также понадобятся WebSockets или Server‑Sent Events, но сначала спроектируйте основной API.
Основные сущности
Тут вы можете на минуту остановиться и перечислить основные сущности вашей системы. Это помогает зафиксировать термины и понять данные, которые лежат в основе дизайна. Это те сущности, которые будет использовать ваш API и которые система будет хранить в модели данных. На самом интервью это может быть буквально короткий маркированный список и пояснение интервьюеру, что это ваш первый черновик, который вы расширите и дополните позднее.
Почему не выписать всю модель данных сразу? Потому что на этом этапе вы пока не знаете, чего вы не знаете. По мере проектирования вы обнаружите новые сущности и связи, о которых не думали заранее. Когда вы перейдете к высокоуровневому дизайну и будете четко понимать, какое состояние меняется при каждом запросе, вы сможете начать выписывать релевантные поля/колонки для каждой сущности.
В примере с Twitter основные сущности довольно простые:
- User
- Tweet
- Follow
Пара полезных вопросов, которые помогут выявить основные сущности:
- Кто является акторами в системе?
- Какие ресурсы нужны, чтобы удовлетворить функциональные требования?
Старайтесь подбирать удачные имена для сущностей. Некоторые интервьюеры используют ваши ответы как шанс понять, насколько вы хороши в одной из самых сложных проблем в ИТ.
Пример API
Для Twitter мы бы выбрали REST и спроектировали эндпоинты, используя основные сущности как ресурсы. Ресурсы должны быть существительными во множественном числе, которые представляют сущности в вашей системе:
POST /v1/tweets
body: {
"text": string
}
GET /v1/tweets/:tweetId -> Tweet
POST /v1/follows
body: {
"followee_id": string
}
GET /v1/feed -> Tweet[]
Обратите внимание, что мы используем имена ресурсов во множественном числе (tweets, а не tweet). User ID берется из токена аутентификации в заголовке запроса, а не из тела запроса или URL path-параметра.
Никогда не полагайтесь на чувствительные данные вроде User ID из тела запроса. Всегда аутентифицируйте запросы и берите User ID из токена аутентификации, а не из пользовательского ввода.
Высокоуровневое проектирование (High Level Design) (~10–15 минут)
Теперь, когда у вас есть ясное понимание требований, сущностей и API системы, можно начинать проектировать высокоуровневую архитектуру. Обычно это означает что мы рисуем прямоугольники и стрелки, показывающие компоненты системы и их взаимодействие. Компоненты - это базовые строительные блоки, такие как сервера, базы данных, кэши и т. п. Это можно делать в специализированном сервисе для рисования, например Excalidraw. Раздел Технологии даст вам представление о самых распространенных компонентах, которые важно знать.
Уточните у рекрутера, какое ПО вы будете использовать на интервью, и потренируйтесь заранее. Не хочется тратить время на то, чтобы разбираться с инструментом прямо на интервью.
Не переусложняйте! Ваша главная цель - спроектировать архитектуру, которая удовлетворяет вашему API и, соответственно, выбранным требованиям. Во многих случаях можно идти по одному эндпоинту за раз и последовательно наращивать дизайн, чтобы удовлетворить каждый из них.
Сохраняйте фокус! Очень часто кандидаты начинают усложнять решение слишком рано, из‑за чего так и не приходят к законченному решению. Сначала сконцентрируйтесь на относительно простом дизайне, который удовлетворяет ключевым функциональным требованиям, а затем добавляйте сложность, чтобы закрыть нефункциональные требования в разделе "Погружение в детали". В high-level дизайне естественно замечать места, где позже можно добавить кэш или очередь сообщений - мы рекомендуем просто отметить такие места устным комментарием и короткой пометкой на доске, а затем двигаться дальше.
Пока вы рисуете дизайн, проговаривайте ход мыслей интервьюеру. Явно описывайте, как данные проходят через систему и как состояние (в БД, кэше, очереди сообщений и т.д.) изменяется при каждом запросе - от API‑запроса до ответа. Когда запрос доходит до базы данных, это хороший момент начать выписывать релевантные колонки/поля для каждой сущности. Можно делать это прямо рядом с соответствующей БД на схеме. Это помогает держать модель рядом с нужным компонентом и облегчает эволюцию дизайна со временем. Не заостряйте внимание на типах данных без особой необходимости - интервьюер обычно и так поймет, а вы лишь потеряете время.
Не тратьте время на документирование каждой колонки/поля в схеме. Например, интервьюер и так знает, что у таблицы User есть имя, email и хеш пароля - не обязательно это записывать. Вместо этого фокусируйтесь на тех полях/колонках, которые особенно важны именно для вашего дизайна.
Для нашего простого примера с Twitter вот как вы могли бы наращивать дизайн по одному эндпоинту за раз:
Погружение в детали (Deep Dives) (~15-25 минут)
Внимательные читатели, вероятно, заметили, что наш простой высокоуровневый дизайн Twitter будет ужасно неэффективен при формировании ленты пользователя. Ничего страшного! Именно такие вещи вы и будете улучшать в разделе Погружение в детали. Теперь, когда у вас есть высокоуровневый дизайн, вы используете оставшиеся ~15-25 минут интервью, чтобы улучшить решение, делая следующее:
- Убедиться, что дизайн удовлетворяет всем нефункциональным требованиям
- Разобрать граничные случаи (edge cases)
- Найти и устранить проблемы и узкие места (bottlenecks)
- Улучшать дизайн, реагируя на вопросы и замечания со стороны интервьюера
Насколько проактивно вы ведете интервью, зависит от вашего уровня. От Middle кандидатов можно ожидать, что интервьюер будет вмешиваться и указывать на места, которые можно улучшить. Senior+ кандидаты должны уметь находить такие места самостоятельно и вести обсуждение.
Например, одно из наших нефункциональных требований для Twitter - масштабирование до >100M DAU. Дальше мы могли бы вести обсуждение вокруг горизонтального масштабирования, добавления кэшей и шардирования базы данных - обновляя дизайн по мере обсуждения. Другое требование - небольшое время формирования ленты. В случае Twitter это наиболее интересная часть задачи: мы бы обсудили разветвление при чтении (fanout‑on‑read) против разветвления при записи (fanout‑on‑write) и использование кэшей.
Распространенная ошибка - пытаться "переговорить" интервьюера на этом этапе. Обсуждать действительно есть что, и для Senior+ уровня проактивность важна, но всегда соблюдайте баланс. Оставляйте интервьюеру пространство, чтобы задавать вопросы и проверять ваш дизайн на прочность. Скорее всего, у него есть конкретные сигналы, которые он хочет получить от вас, и вы их пропустите, если будете слишком заняты монологом. Кроме того, вы ухудшите оценку по коммуникации и сотрудничеству.