Привет, Хаброжители! Предлагаем вашему вниманию перевод детального руководства о подготовке к собеседованию по LLD (Low-level design). Автор как будто из интереса посещает собеседования по проектированию систем, и в этом руководстве обобщил свой опыт о том, чего стоит ждать и к чему готовиться в зависимости от той позиции, на которую вы претендуете. Вам решать, насколько такая практика себя оправдывает, но, как говорится – «предупреждён – значит, вооружён».
Я работаю ведущим инженером-программистом в компании LocoNav. Проходя собеседования, обладая таким профессиональным опытом (около 9 лет), я могу претендовать на следующие позиции:
Обязанности на каждой из этих позиций сильно зависят от потребностей организации. Ничего страшного, если в долгосрочной перспективе я перейду с должности лида на должность сеньора, хотя, кому-то так и не кажется (об этом рассказал здесь). Но, в зависимости от позиции, на которую претендует соискатель, раунд собеседования, посвящённый низкоуровневому проектированию (Low-Level Design, далее LLD). может проходить очень по-разному. Далее в этой статье мы поговорим о LLD применительно к собеседованиям, но я считаю, что этот навык пригодится вам и в повседневной работе. Не стоит что-то учить только ради собеседования.
Из Википедии:
Я неслучайно привёл здесь это формальное определение. К сожалению, из-за привычки изучать тему с прицелом на собеседованиe, многие постепенно перестали следовать пошаговой сути низкоуровневого проектирования. Зачастую собеседующий почему-то ожидает от вас, что за 40-50 минут, отведённых вам на тестовое задание, вы представите самый лучший дизайн с рабочим кодом, покрытым тестами. Да, иногда это достижимо, если специально практиковаться в решении типичных задач, но это совсем не соответствует понятию «проектирование» в программной инженерии.
Я за всю карьеру проходил примерно 5-6 собеседований на LLD, и все они были непохожи друг на друга. Сначала расскажу, как я готовился к этим собеседованиям и прокачивал навыки низкоуровневого проектирования в целом. Затем поделюсь опытом, приобретённым на разных собеседованиях по LLD, которые я проходил. Наконец, охарактеризую разные категории задач, которые попадались мне в разных плейлистах, курсах и интервью.
Где-то в конце 2023 года на меня вышли рекрутеры Atlassian по поводу работы в Instahyre, там искали человека на должность старшего программиста (сеньора). На тот момент я был совершенно не готов к такому собеседованию и почти не представлял себе, как в общем устроен его процесс. Рекрутеры, общаясь со мной по телефону, объяснили этот процесс, а также поделились документами, которые должны были помочь мне лучше разобраться в каждом раунде. Я сориентировался и понял, что с технической точки зрения их интересуют три основные темы: структуры данных, низкоуровневое проектирование (LLD) и высокоуровневое проектирование (HLD/проектирование систем). Проработав каждый раунд в деталях, я пришёл к выводу, что, в принципе, работал со всеми темами, которые понадобятся на собеседовании, но готовиться нужно так, чтобы уметь подать эту информацию в ходе собеседования.
Я поступил как все — стал искать бесплатный контент. На эту тему есть множество отличных видео на YouTube, но качество у них разное. Я столкнулся с типичной проблемой избытка информации — выбери любую тему для изучения, и на YouTube найдётся 100 человек, которые по ней уже высказываются. Не сказать, что все на подбор, но почти никто из них не говорит неверных вещей. Здесь нужно самостоятельно выбрать одного-двух авторов и учиться у них (иначе просто запутаетесь).
Во всех каналах/плейлистах, где учат LLD, прослеживалась такая закономерность:
Думаю, это довольно неплохая дорожная карта, если вы нацелены только на собеседование. Порешайте 5-10 задач, только не смотрите в решения до тех пор, пока хотя бы раз не попытаетесь каждую задачу самостоятельно. Если заглянуть в решения, то они сразу кажутся совершенно очевидными, и у вас возникает ложное чувство уверенности, будто и так всё понятно. Я оказывался в такой ситуации как на LLD, так и на HLD — ощущение, будто я всё знаю, хотя, просто посмотрел видео. Только после того, как мне отказали в нескольких хороших компаниях, я осознал, что всё не так очевидно.
Немного остановлюсь на каждом из пунктов, прежде, чем пойдём дальше:
Как я уже говорил, я многократно проходил собеседования в разных компаниях на разные должности. Поэтому поделюсь, чего следует ожидать на каждом уровне:
Приведу пример. В ходе собеседования в Atlassian меня попросили написать код для игры в змейку. Поскольку речь шла о позиции сеньора, они смотрели, насколько быстро я программирую — ожидали, что я выдам полностью рабочий код за 45 минут (плюс мы коротко обсудим, как бы я его протестировал).
В то же время, при собеседовании на роль ведущего инженера программировать вообще не пришлось. Мы в основном обсуждали системные сущности (модели), как они связаны, немного поговорили о проектировании баз данных, проектировании API и о некоторых конкретных частях системы, которые казались непростыми (назову это центральной проблемой). Поэтому очень важно с самого начала понимать, на что рассчитывает интервьюер. Как правило, экзаменатор должен сам вам чётко объяснить, какой соискатель интересует компанию. Но я пытался устроиться и в относительно небольшие фирмы, где такого не происходило, поэтому думаю, что всегда лучше разъяснить, чем предполагать. Есть эмпирическое правило: никаких априорных допущений на собеседовании (независимо от раунда). Если они у вас всё-таки есть, то поделитесь ими с интервьюером — так он сможет сразу вам указать, в чём вы заблуждаетесь. Когда у вас всего 40-50 минут, вам могут очень дорого обойтись любые неверные допущения, которые придётся корректировать на ходу.
Поделюсь подборкой таких тем, которые затрагивались в моих собеседованиях по LLD в разных компаниях. Как только вводная часть закончена, собеседующий формулирует вам задачу. Вот чему я уделяю особое внимание:
Итак, именно об этом обычно спрашивают на собеседованиях по LLD. Здесь я также хотел бы добавить: мне попадались три категории задач, которые обычно задают на собеседованиях:
Сложность разных проблем также отличается. Например, вам будет просто рассуждать о таком приложении, каким вы сами часто пользуетесь (например, Uber), чем обсуждать систему PubSub. Рекомендую вам попрактиковаться в решении всех перечисленных здесь типов задач, чтобы вы могли составить впечатление о сложности каждой из них. Хотя, в общем виде все рекомендации остаются без изменений — такими, как я описал их выше. Удачи в обучении.
Спасибо вам за то, что прочитали эту подробную статью о низкоуровневом проектировании. Если она вам понравилась, то, пожалуйста, загляните ещё и сюда:
Книга по теме «System Design: пережить интервью»
Контекст
Я работаю ведущим инженером-программистом в компании LocoNav. Проходя собеседования, обладая таким профессиональным опытом (около 9 лет), я могу претендовать на следующие позиции:
- Программист / старший программист (сеньор),
- Ведущий программист / лид,
- Менеджер команды разработчиков/SEM (если я захочу развиваться как управленец).
Обязанности на каждой из этих позиций сильно зависят от потребностей организации. Ничего страшного, если в долгосрочной перспективе я перейду с должности лида на должность сеньора, хотя, кому-то так и не кажется (об этом рассказал здесь). Но, в зависимости от позиции, на которую претендует соискатель, раунд собеседования, посвящённый низкоуровневому проектированию (Low-Level Design, далее LLD). может проходить очень по-разному. Далее в этой статье мы поговорим о LLD применительно к собеседованиям, но я считаю, что этот навык пригодится вам и в повседневной работе. Не стоит что-то учить только ради собеседования.
Что такое LLD?
Из Википедии:
Низкоуровневое проектирование (LLD) — это процесс проектирования на уровне компонентов, заключающийся в пошаговом улучшении системы. Этот процесс может использоваться при проектировании структур данных, требуемой программной архитектуры, исходного кода, а в конечном счёте — и алгоритмов повышения производительности.
Я неслучайно привёл здесь это формальное определение. К сожалению, из-за привычки изучать тему с прицелом на собеседованиe, многие постепенно перестали следовать пошаговой сути низкоуровневого проектирования. Зачастую собеседующий почему-то ожидает от вас, что за 40-50 минут, отведённых вам на тестовое задание, вы представите самый лучший дизайн с рабочим кодом, покрытым тестами. Да, иногда это достижимо, если специально практиковаться в решении типичных задач, но это совсем не соответствует понятию «проектирование» в программной инженерии.
Я за всю карьеру проходил примерно 5-6 собеседований на LLD, и все они были непохожи друг на друга. Сначала расскажу, как я готовился к этим собеседованиям и прокачивал навыки низкоуровневого проектирования в целом. Затем поделюсь опытом, приобретённым на разных собеседованиях по LLD, которые я проходил. Наконец, охарактеризую разные категории задач, которые попадались мне в разных плейлистах, курсах и интервью.
Изучаем LLD и совершенствуем навыки
Где-то в конце 2023 года на меня вышли рекрутеры Atlassian по поводу работы в Instahyre, там искали человека на должность старшего программиста (сеньора). На тот момент я был совершенно не готов к такому собеседованию и почти не представлял себе, как в общем устроен его процесс. Рекрутеры, общаясь со мной по телефону, объяснили этот процесс, а также поделились документами, которые должны были помочь мне лучше разобраться в каждом раунде. Я сориентировался и понял, что с технической точки зрения их интересуют три основные темы: структуры данных, низкоуровневое проектирование (LLD) и высокоуровневое проектирование (HLD/проектирование систем). Проработав каждый раунд в деталях, я пришёл к выводу, что, в принципе, работал со всеми темами, которые понадобятся на собеседовании, но готовиться нужно так, чтобы уметь подать эту информацию в ходе собеседования.
Я поступил как все — стал искать бесплатный контент. На эту тему есть множество отличных видео на YouTube, но качество у них разное. Я столкнулся с типичной проблемой избытка информации — выбери любую тему для изучения, и на YouTube найдётся 100 человек, которые по ней уже высказываются. Не сказать, что все на подбор, но почти никто из них не говорит неверных вещей. Здесь нужно самостоятельно выбрать одного-двух авторов и учиться у них (иначе просто запутаетесь).
Во всех каналах/плейлистах, где учат LLD, прослеживалась такая закономерность:
- Учим принципы SOLID,
- Учим паттерны проектирования,
- Выбираем 5-10 распространённых задач (игра «Крестики-нолики», игра «Лила», «система лифтов», приложение по разделению счетов (Splitwise), платформа для проката автомобилей, т.д). Задавая такие задачи, вас пытаются проверить на знание минимум одного-двух паттернов проектирования в каждой.
Думаю, это довольно неплохая дорожная карта, если вы нацелены только на собеседование. Порешайте 5-10 задач, только не смотрите в решения до тех пор, пока хотя бы раз не попытаетесь каждую задачу самостоятельно. Если заглянуть в решения, то они сразу кажутся совершенно очевидными, и у вас возникает ложное чувство уверенности, будто и так всё понятно. Я оказывался в такой ситуации как на LLD, так и на HLD — ощущение, будто я всё знаю, хотя, просто посмотрел видео. Только после того, как мне отказали в нескольких хороших компаниях, я осознал, что всё не так очевидно.
Немного остановлюсь на каждом из пунктов, прежде, чем пойдём дальше:
- Принципы SOLID — они служат базой для качественного дизайна, если вы реализуете ваше приложение на объектно-ориентированном языке. Лично я очень высоко ценю принцип единственной ответственности — пожалуй, более, чем любое другое правило или паттерн проектирования. Думаю, что при хорошем понимании этого принципа вы сможете писать очень аккуратные классы и обеспечивать, чтобы между классами не происходило никакого ненужного разделения кода/свойств. Не буду здесь детально объяснять эти принципы, а рекомендую вам почитать о них в этой отличной статье.
- Паттерны проектирования – представляют собой ни что иное как решения типичных проблем, с которыми приходится сталкиваться при объектно-ориентированном проектировании. Например, в больших приложениях обычно приходится писать многоуровневые абстракции, основанные на некоем языке или фреймворках. Проектируя такие классы, приходишь к выводу, что нужно предоставить конечному пользователю как можно более простой и удобный интерфейс (API). Такой паттерн называется фасад. Я давно стал конструировать абстракции в моих проектах при помощи этого паттерна, даже до того, как систематически взялся за изучение паттернов проектирования. Если у вас 3 и более лет опыта в программировании, весьма вероятно, что вы читали книгу «Паттерны объектно-ориентированного проектирования». Этот труд считается основополагающим в своей теме. Ещё мне повезло попасть на этот сайт (кстати, у них есть и своя книга в формате PDF) с очень хорошими примерами и иллюстрациями, которые пригодятся всем, желающим изучить паттерны проектирования. У этого сайта есть т дополнительный бонус, который я затрону в следующем пункте.
- (Бонус) запахи кода — прежде, чем мне удалось найти хороший материал по запахам кода, я прочитал несколько статей о них в разных блогах. Как правило, в онлайн-курсах никто не рекомендует читать о запахах кода, но я обещал, что поделюсь с вами информацией не только о собеседованиях, которая действительно поможет вам преуспеть в LLD. Поэтому советую почитать о распространённых запахах кода и о том, как обычно устраняется каждый из них.
- Распространённые задачи по программированию – выберите любой плейлист на YouTube — и найдёте авторов, рассуждающих, как написать собственную маленькую версию Uber, ZoomCar, BookMyShow, Splitwise и пр. Категорически рекомендую вам перед походом на собеседование самостоятельно решить не менее 5-7 задач такого рода (не заглядывая в решения). Такое упражнение поможет вам убедиться, что вы умеете применять паттерны, принципы, а также понимаете продукт в достаточной степени, чтобы хорошо справляться с низкоуровневым проектированием. Не могу однозначно порекомендовать конкретный материал, но поделюсь вот этим на случай, если вы хотите составить представление об этих проблемах.
Идём глубже
Как я уже говорил, я многократно проходил собеседования в разных компаниях на разные должности. Поэтому поделюсь, чего следует ожидать на каждом уровне:
- Рядовой программист/сеньор – акцент делается на машинном программировании. Поэтому о вас судят в основном по тому, насколько вы разбираетесь в структурах данных и в низкоуровневом проектировании.
- Ведущий/Главный/Кризисный менеджер – акцент делается на высокоуровневом проектировании (систем) и лидерских качествах. Умение программировать вручную имеет меньший вес по сравнению с HLD, либо по сравнению с умением писать код на более младших ролях. Вас скорее попросят спроектировать схему, API или любую нетривиальную часть системы, о которой требуется поговорить.
Приведу пример. В ходе собеседования в Atlassian меня попросили написать код для игры в змейку. Поскольку речь шла о позиции сеньора, они смотрели, насколько быстро я программирую — ожидали, что я выдам полностью рабочий код за 45 минут (плюс мы коротко обсудим, как бы я его протестировал).
В то же время, при собеседовании на роль ведущего инженера программировать вообще не пришлось. Мы в основном обсуждали системные сущности (модели), как они связаны, немного поговорили о проектировании баз данных, проектировании API и о некоторых конкретных частях системы, которые казались непростыми (назову это центральной проблемой). Поэтому очень важно с самого начала понимать, на что рассчитывает интервьюер. Как правило, экзаменатор должен сам вам чётко объяснить, какой соискатель интересует компанию. Но я пытался устроиться и в относительно небольшие фирмы, где такого не происходило, поэтому думаю, что всегда лучше разъяснить, чем предполагать. Есть эмпирическое правило: никаких априорных допущений на собеседовании (независимо от раунда). Если они у вас всё-таки есть, то поделитесь ими с интервьюером — так он сможет сразу вам указать, в чём вы заблуждаетесь. Когда у вас всего 40-50 минут, вам могут очень дорого обойтись любые неверные допущения, которые придётся корректировать на ходу.
Поделюсь подборкой таких тем, которые затрагивались в моих собеседованиях по LLD в разных компаниях. Как только вводная часть закончена, собеседующий формулирует вам задачу. Вот чему я уделяю особое внимание:
- Делать заметки: я привык делать заметки на всех технических собеседованиях, которые проходил. В обычном текстовом редакторе, открытом в отдельном окне (по умолчанию не первый год пользуюсь для этого Sublime Text). Можно и ручкой на бумаге записывать, но в данном случае важно, чтобы и интервьюер мог сразу прочитать, что вы пишете. По той же причине я в самом начале собеседования расшариваю экран. Это помогает конкретизировать постановку задачи ещё до того, как переходить к решению её конкретных частей.
- Обрисовать требования: итак, первым делом от меня требовалось понять, из каких частей состоит та система, которую мы собираемся делать. Возьмём реалистичный пример — Uber, ZoomCar или BookMyShow (допустим, BMS). Полностью обсудить такой продукт за 45 минут невозможно. Поэтому лучше сразу уточнить у собеседника, что его интересует. Например, при BMS-подобной задаче центральной проблемой является бронирование билетов, а не обсуждение типов мест, например, в зрительном зале. Аналогично, если мы обсуждаем платформу для заказа такси, такую, как Uber, то центральной проблемой будет подбор водителя, а не функция построения маршрута в городе. В данном случае требуется мыслить, исходя из продукта. Если вы уже пользовались конкретным приложением, то должны представлять его основные возможности (а также ключевую задачу, которую оно решает), но, если не пользовались — так и скажите, чтобы вы вместе с вашим собеседником могли конкретизировать задачу. Когда это сделано, возможно два варианта развития событий: может быть, вас попросят написать код, а может и нет.
- Требуется программировать – если вас попросили написать код, то сначала обдумайте ключевые сущности (модели), их взаимодействия, как они связаны друг с другом. Спросите у интервьюера, правильно ли всё понимаете. Если всё так — начинайте писать код. Причём, пишите его по нисходящему принципу, когда сначала делаются общие наброски решения, а уже во вторую очередь вы берётесь за реализацию ключевого алгоритма. Как только структура примет приличный вид, начинайте заполнять её реализациями методов (в зависимости от того, сколько времени у вас осталось).
- Программировать не требуется – если вам не озвучивали никаких ожиданий, спросите сами, потребуется ли писать код, либо предполагается просто поговорить об API, моделях и какой-то ключевой проблеме. Если писать рабочий код точно не требуется, всё равно не забудьте упомянуть модели и сервисы, так как именно с них начинается проектирование любой базы данных. В таком случае обязательно подробно обсудите форматы запроса и отклика. На некоторых собеседованиях вас будут проверять на понимание REST API, особенно, если поставленная задача связана с обработкой API (есть и задачи, не относящиеся к вебу, об этом позже). Говоря об API, обязательно озвучьте ваши допущения (если они у вас есть). Например: «полагаю, уже есть готовый механизм аутентификации, и аутентификационная информация передаётся в заголовках к каждому из API, о которых мы говорим».
- Схема базы данных – на некоторых собеседованиях вас могут попросить прямо назвать конкретные таблицы и столбцы. Так, в задаче на BMS у вас есть город со множеством кинотеатров. В кинотеатре есть множество кинозалов. В каждом зале за день может идти несколько сеансов и имеется множество мест. Надеюсь, идею вы поняли: термины, выделенные жирным — это наши модели. Поэтому интервьюер может попросить вас начертить эти таблицы, их столбцы (как минимум, самые важные), чтобы убедиться, что вы правильно проектируете схему. Вас также могут попросить написать запросы к конкретным API, чтобы проверить, соответствует ли спроектированный вами вариант требованиям системы (напр., напишите запрос, проверяющий, полностью ли распроданы билеты на заданный сеанс в конкретном кинотеатре).
- Центральная проблема – в разных системах такие проблемы отличаются. Например, если вы пишете приложение для разделения счетов, такое, как Splitwise, то центральная проблема сводится к распределению сумм в группе людей. При проектировании приложения вроде Uber речь зайдёт о поиске водителей и отображении расчётного времени прибытия. На данном этапе вас могут спрашивать о самых разных вещах в контексте конкретной задачи — от алгоритмов до паттернов проектирования. По мере углубления в задачу вы, возможно, сумеете лучше понять центральную проблему. Может быть, собеседующий и сам вам подскажет: а давайте вот это обсудим.
- Тестовые кейсы/сценарии – от хорошего разработчика, в частности, ожидается, что он умеет качественно тестировать свой код. Если вы уже написали код, и у вас осталось время, вас могут попросить добавить несколько тестов. В иных случаях с вами рассчитывают обсудить только некоторые особенно важные тестовые сценарии. Тут особенно нечего добавить. Если вы умеете правильно дробить классы, придерживаетесь паттернов проектирования и знаете, как покрывать код тестами, этого должно хватить. Если вы считаете, что в задаче есть часть, где потребовалось бы специально протестировать некоторые пограничные случаи, обязательно об этом скажите.
Заключение
Итак, именно об этом обычно спрашивают на собеседованиях по LLD. Здесь я также хотел бы добавить: мне попадались три категории задач, которые обычно задают на собеседованиях:
- Простые игры, основанные на понятной логике: змейка, лила, крестики-нолики, шахматы, т.д.
- Популярные продукты – заказ такси (Uber), прокат машин (Zoom Car), какая-нибудь платформа по бронированию билетов (BookMyShow), т.д.
- Технические системы – система лимитирования запросов, кэш с вытеснением реже всего используемых элементов (LRU), система «публикация-подписка» (PubSub), база данных вида «ключ-значение», т.д.
Сложность разных проблем также отличается. Например, вам будет просто рассуждать о таком приложении, каким вы сами часто пользуетесь (например, Uber), чем обсуждать систему PubSub. Рекомендую вам попрактиковаться в решении всех перечисленных здесь типов задач, чтобы вы могли составить впечатление о сложности каждой из них. Хотя, в общем виде все рекомендации остаются без изменений — такими, как я описал их выше. Удачи в обучении.
Спасибо вам за то, что прочитали эту подробную статью о низкоуровневом проектировании. Если она вам понравилась, то, пожалуйста, загляните ещё и сюда:
- Я создал небольшой репозиторий на github с решениями четырёх задач по LLD на языках Ruby и Java. Перейдя к любой из этих задач, вы найдёте файл README, в котором описано, что именно мы создаём. Можете считать этот файл README описанием требований к задаче. В мой код заглядывайте, только если застрянете.
- Все мои посты по подготовке к собеседованиям и об опыте собеседований в целом выложены здесь.
Книга по теме «System Design: пережить интервью»