Прояснение требований
Интервью начинается с дизайна, который ты не рисуешь. Раздели функциональные и нефункциональные требования, назови ключевые сущности и паттерны чтения/записи, зафиксируй цели по согласованности, задержке и доступности — архитектуру выбирают эти ограничения, а не коробки.
Кандидат слышит «спроектируй Twitter» и уже через тридцать секунд рисует коробки: балансировщик, app-слой, база. Через десять минут интервьюер спрашивает: «А как держать свежей ленту знаменитости со 100M подписчиков?» — и весь дизайн рушится, потому что его никогда не строили под этот вопрос. Кандидат решил задачу, которую никто не ставил. Сильный кандидат на те же три слова первые пять минут ничего не рисует: Кто постит, а кто читает? Лента реального времени или согласованность в конечном счёте? Мы проектируем постинг, ленту, поиск — или всё сразу? Он не тянет время. Он решает, какую систему строить, прежде чем её строить.
Дизайн, который ты не рисуешь
Интервью по системному дизайну — не проверка того, сколько компонентов ты назовёшь. Это проверка того, строишь ли ты правильную систему — а «правильная» полностью определяется требованиями, которые интервьюер не озвучит сам. Стартовый промпт («спроектируй чат», «спроектируй ride-share») намеренно недоопределён, как настоящий продуктовый бриф. Первый ход сеньора одинаков и на интервью, и в дизайн-доке на работе: не фиксировать структуру, пока задача не отскоплена.
Это важно, потому что архитектура — следствие ограничений, а не фич. «Twitter» с бюджетом устаревания в 10 секунд и «Twitter» со строгим реальным временем — две разные системы: одна может делать fan-out на запись и агрессивно кэшировать, другая нет. Ты не нарисуешь вторую коробку верно, пока не знаешь, в какой ты системе. Пропуск требований — самая частая причина, по которой сильный технически кандидат проваливает интервью: он строит нечто внушительное, отвечающее на неверный вопрос.
Функциональные против нефункциональных
Раздели требования на две кучи, потому что они ограничивают дизайн по-разному. Пропустишь это разделение — и через пятнадцать минут окажешься глубоко в дизайне кэша, не договорившись, нужна ли вообще долговечность данных.
Функциональные требования — это что система делает: фичи, как глаголы, которые выполняет пользователь. Для сокращателя ссылок: создать короткую ссылку, перенаправить её на цель, опционально показать аналитику кликов. Они задают поверхность API и сущности. Ошибёшься в них — построишь не тот продукт.
Нефункциональные требования — это насколько хорошо она это делает: качества, как числа и цели. Масштаб (сколько пользователей, какой QPS — queries per second, запросов в секунду), задержка (бюджет p99), доступность (SLO — см. юнит о доступности), согласованность (строгая или в конечном счёте), долговечность (можем ли вообще терять данные) и стоимость. Ошибёшься в них — построишь правильный продукт в форме, которая не переживёт встречу с продакшеном.
Ловушка — воспринимать интервью как чисто функциональное («оно даёт постить и читать»). Функциональные требования обычно лёгкие и одинаковы у всех кандидатов. Нефункциональные требования — вот где дизайн реально решается, и где интервьюер смотрит, знаешь ли ты это.
▸Почему это работает
Почему доминируют нефункциональные требования? Потому что две системы с идентичными фичами могут иметь радикально разные архитектуры, как только меняются цели. «Спроектируй key-value хранилище», терпящее согласованность в конечном счёте и устаревание в 5 минут, — это кэш; тот же промпт, требующий линеаризуемых чтений и нулевой потери данных, — это БД с репликацией через консенсус, с совсем другой стоимостью и задержкой. Фичи («get», «put») идентичны — но долговечность, согласованность и задержка толкают от одной схемы коробок к совершенно другой. Это тот же урок, что CAP/PACELC из юнита о распределении данных: гарантии не выбирают свободно — их обменивают, и требования говорят, какой обмен тебе разрешён.
Скоупь к тому, что важно
Недоопределённый промпт ещё и слишком большой. «Спроектируй YouTube» содержит загрузку, транскодирование, хранение, рекомендательную ленту, поиск, комментарии, монетизацию и live-стриминг — каждое само по себе многонедельный дизайн. Всё это за 45 минут не спроектировать, и попытка сигналит, что ты не умеешь приоритизировать.
Поэтому скоупишь вслух и даёшь интервьюеру рулить. Назови кандидат-фичи, предложи одну-две интересные ядровые и подтверди: «Тут есть загрузка, воспроизведение, рекомендации и поиск. Я бы сосредоточился на загрузке-и-воспроизведении при масштабе, ведь там живут вызовы хранения и доставки — это то, куда вы хотите копнуть?» Это делает три вещи: показывает, что ты видишь, где живёт сложность; даёт интервьюеру шанс перенаправить на важную ему часть; и ограничивает задачу, чтобы остаток сессии был глубоким, а не поверхностным-везде. Отскопленный дизайн, взятый вглубь, бьёт полный дизайн, взятый в никуда.
Ключевые сущности и паттерны доступа
Какую сущность система вообще хранит — и как что-либо её читает или пишет? Не ответишь на это до коробок — коробки будут неверными.
После скоупа назови ключевые сущности — существительные, которые система хранит. Для ride-share: Rider, Driver, Trip, Location. Для чата: User, Conversation, Message, Membership. Это короткий список (обычно 3–6), и это зародыш модели данных. Назвать их рано — заставить себя чётко понять, какое состояние вообще существует.
Затем — и этот ход большинство кандидатов пропускает — охарактеризуй паттерны доступа: для каждой сущности, как её читают и пишут? Чтений больше или записей? По какому ключу? В какой форме (одиночный lookup, range-скан, join, fan-out)? Горячие ключи или равномерно? Это самый нагруженный вопрос, который ты можешь задать, потому что он определяет хранилище и индексы ещё до того, как нарисована хоть одна коробка.
сущность паттерн записи паттерн чтения отношение
─────────────────────────────────────────────────────────────────────────────
Message append, по conversation range-скан, свежие первыми запись ≈ чтение
Timeline fan-out при новом посте одно чтение, по user, горячо чтение >> запись (100:1)
Trip create + апдейты статуса lookup по id, гео-запрос запись-ish, низкий объёмЛента, которую читают в 100× чаще, чем пишут, хочет предвычисленный, кэшированный путь чтения; write-heavy лог событий хочет хранилище под append. Паттерн доступа, а не сущность, выбирает базу. Здесь окупается рефлекс «прикидки на салфетке» из юнита масштабируемости: отношение чтение/запись плюс оценка QPS — вот что превращает «нам нужна база» в «нам нужна такая база».
Формулируй цели числами
Размытые нефункциональные требования бесполезны. «Должно быть быстро» и «должно быть высокодоступно» ничего не ограничивают. Переведи каждое в число, под которое можно проектировать:
- Задержка: бюджет p99, не среднее (урок latency-vs-throughput — среднее прячет хвост). «Чтения p99 ≤ 100 мс, записи можно медленнее.»
- Доступность: SLO с бюджетом ошибок (урок SLA/SLO/SLI). «99,9% — около 43 минут даунтайма в месяц.»
- Согласованность: выбери точку на спектре и скажи почему. «Посты должны быть read-your-writes для автора; другие пользователи терпят несколько секунд устаревания.» Это одно предложение разрешает кэширование и асинхронный fan-out — или запрещает.
- Масштаб: числа, что меняют дизайн (следующий урок). DAU, QPS, рост данных за годы.
Вместе эти четыре числа полностью фиксируют пространство дизайна: без цели по задержке не обосновать кэш; без выбора согласованности не решить, разрешён ли fan-out (асинхронное распространение данных подписчикам) вообще. Проговорить это вслух — не педантизм: каждое число — рычаг, включающий одну архитектуру и выключающий другую, и назвать их — это как ты с интервьюером договариваетесь, какую систему вы реально строите. Поймаешь себя за размытым прилагательным («быстро», «надёжно») — останавливайся и заменяй его числом: пока не сделал, архитектура не выбрана.
▸Частая ошибка
Самая дорогая ошибка в требованиях — выдумать ограничения, которых интервьюер не просил, обычно это переинженеринг. Кандидат слышит «спроектируй сокращатель ссылок для внутреннего инструмента» и сразу проектирует под миллиард QPS, глобальную мультирегиональную репликацию и строгую согласованность, сжигая всю сессию на механику, которой задаче никогда не требовалось. Золочение — такой же провал, как недоскоуп: оно показывает, что ты не умеешь подгонять решение под ограничения. Лечение — та же дисциплина наоборот: спрашивай масштаб, а не предполагай максимальный, и проектируй под данное требование, с одним предложением о том, как оно изменится при 100×. Строй под задачу перед тобой, а не под самую внушительную, какую можешь вообразить.
Тебя просят «спроектировать чат». Какой вопрос сильнее всего меняет архитектуру и должен быть задан первым?
Для социальной ленты чтений ~100:1 больше, чем записей. Что этот паттерн доступа сильнее всего оправдывает, ещё до того как нарисована коробка?
Две кучи требований ограничивают дизайн по-разному: _______ требования — это что система делает (фичи, как глаголы пользователя), а нефункциональные — насколько хорошо она это делает (масштаб, задержка, доступность, согласованность — числами), и именно вторые обычно решают архитектуру.
- 01В чём разница функциональных и нефункциональных требований, и почему дизайн решают нефункциональные?
- 02Зачем характеризовать паттерны доступа и как они меняют дизайн?
- 03Как скоупить слишком широкий промпт и каков провал на каждом краю?
Интервью по системному дизайну решается в первые минуты, до того как нарисована коробка. Раздели требования на функциональные (что делает — фичи, как глаголы пользователя) и нефункциональные (насколько хорошо — масштаб, p99 задержка, SLO доступности, согласованность, долговечность, стоимость, числами), и помни: нефункциональные цели — вот где реально выбирают архитектуру: идентичные фичи могут требовать совершенно разных систем при сдвиге целей (теряющее данные согласованное-в-конечном-счёте хранилище — кэш; линеаризуемое и долговечное — БД на консенсусе, обмен CAP/PACELC из юнита распределения данных). Скоупь слишком широкий промпт вслух и дай интервьюеру направить к интересному ядру — отскопленный дизайн вглубь бьёт полный в никуда. Назови 3-6 ключевых сущностей и, главное, их паттерны чтения/записи, потому что паттерн доступа (не сущность) выбирает хранилище и путь чтения — read-heavy лента 100:1 хочет предвычисленный кэшированный путь чтения. И формулируй каждую цель числом, под которое можно проектировать. Два провала симметричны: недоскоуп решает не ту задачу, а золочение строит механику, которой задаче не нужно — оба суть неумение подогнать дизайн под ограничения. Теперь, когда слышишь размытый промпт, первый ход — остановиться, задать четыре вида вопросов и записать ограничения прежде, чем нарисована любая коробка.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.
Примени это
Примени этот урок в реальном проекте.