Что реально спрашивают на собеседовании фронтенд-стажёра
Команда ZUB-AI посмотрела запись реального технического собеседования на позицию стажёра full-stack разработчика с уклоном во фронтенд. Формат простой и честный: без постановочных сцен, с реальными паузами, ошибками кандидата и живой реакцией интервьюера. За две недели до записи этого ролика было отсмотрено около 60 заявок и проведено 16 собеседований — то есть перед нами не разовая история, а выжимка из целой воронки найма.
Кому это полезно? Если вы готовитесь к позиции junior/intern frontend или fullstack, это почти готовый чек-лист вопросов. Если вы сами нанимаете стажёров — это пример структуры интервью, которую можно взять за основу. Главный вывод одним предложением: сильная теория и хорошие пет-проекты перевешивают провал на алгоритмах, но полное отсутствие прогресса в live coding — это всё равно тревожный звонок.
Дальше разберём собеседование по блокам: проекты, React, TypeScript, бэкенд-интеграция, NestJS и живое кодирование. В конце — практический чек-лист и наши наблюдения о том, где подходы к найму сходятся, а где расходятся.
Почему проекты важнее резюме
Первое, на что смотрит интервьюер — не список технологий в резюме, а конкретные пет-проекты. Логика простая: знания, которые не применялись на практике, забываются или остаются поверхностными. Кандидат в разборе — это человек с опытом около 5 лет с перерывами, который начал путь с моддинга игр («стимка»), а серьёзно погрузился в разработку курсе на третьем.
Из его портфолио выделяются три вещи:
- Проект авторизации с access/refresh токенами через Redis, всё самописное кроме JWT-библиотеки.
- Микросервисная архитектура через Consul: регистрация портов, конфигурация через dependency injection, общение сервисов через единый инстанс Redis. Отдельно пришлось городить собственный фильтр исключений — потому что exception при передаче между сервисами сериализуется через раз.
- Образовательная платформа про кино, где бэкенд почти готов, а фронтенд ещё не начат. Первая версия была признана самим кандидатом неудачной — «полная лажа» с проблемами транзакций, поэтому проект переписывался заново.
Самая сложная задача, которую выделил кандидат — ротация JWT-токена при обновлении страницы. Токен не обновлялся из-за того, что interceptor работает не на том уровне жизненного цикла, что рендеринг компонента. Мелочь, а голову сломать можно легко — знакомая история для любого, кто городил свою авторизацию с нуля.
Вывод для читателя: если у вас нет fullstack-опыта, но есть один доведённый до конца проект с нетривиальной архитектурой — это весит больше, чем десять недоделанных клонов Netflix.
React: что спрашивают и где обычно спотыкаются
Кандидат оценил себя на 5 из 10 по React, объяснив это перерывами в практике до полугода. Это честная и разумная самооценка — здесь важнее не цифра, а готовность признать пробелы.
Вопросы, которые звучали:
- Чем React/Vue лучше чистого JS — актуальность данных, клиентский рендеринг, Virtual DOM, управление состоянием через хуки.
- Опыт со стейт-менеджерами: Zustand («любимый, потому что простой»), Redux (знает всю экосистему), но Context API — слабее.
- useMemo vs useCallback — тут кандидат ответил корректно: useMemo кеширует результат вычисления при неизменных зависимостях, useCallback сохраняет стабильную ссылку на функцию между рендерами, чтобы
React.memoдействительно работал, а не перерендеривал компонент зря. - Классический кейс: родитель передаёт функцию дочернему компоненту через
React.memo. БезuseCallbackдочерний компонент всё равно перерендерится — потому что при каждом рендере родителя создаётся новая ссылка на функцию, иmemoэто ловит как изменение пропсов. - Виртуализация списков — концепция, где на экране рендерятся только видимые элементы (условно 10 из 1000), а при скролле верхние элементы размонтируются, нижние монтируются, расчёт идёт через estimated size и индексы. Кандидат объяснил идею верно, но признался, что саму библиотеку (речь про react-virtualized) до конца не разбирал.
- Условия ре-рендера компонента: изменение состояния, изменение контекста при подписке, ре-рендер родителя — даже если пропсы дочернего компонента не изменились.
Заметьте: почти все вопросы — не про синтаксис, а про то, почему React ведёт себя так, а не иначе. Это типичный признак сильного технического интервью — синтаксис гуглится за секунду, а понимание механизма ре-рендеров — нет.
TypeScript: структурная типизация и антипаттерны
Самооценка кандидата — 6-7 из 10, интервьюер отметил, что кандидат себя занизил. Разберём, что спрашивали.
Первый блок — зачем вообще нужен TypeScript. Классический пример: в JS "2" + 2 даёт строку "22", потому что слабая динамическая типизация позволяет складывать что угодно со всем подряд. TypeScript ловит такие вещи на этапе компиляции, а не в рантайме у пользователя.
Второй момент — структурная типизация. TypeScript сравнивает типы по набору полей, а не по имени типа. Если два интерфейса содержат одинаковые поля, объект технически может имплементировать оба одновременно. Это принципиально отличается от номинальной типизации (например, в C#), где типы сравниваются по имени объявления, а не по структуре.
Дальше — живой разбор Omit и Exclude прямо в редакторе кода. Кандидат сначала перепутал их назначение, пытаясь применить Exclude к полям объекта. Совместно с интервьюером разобрались: Exclude работает с union-типами (убирает часть вариантов из объединения), а для исключения полей объекта нужен именно Omit. Разница небольшая на бумаге, но путаница между ними — частая ошибка даже у людей с опытом.
Антипаттерны, которые назвал кандидат:
any— ломает всю систему типов, потому что отключает проверку целиком.as(type assertion) — по сути говорит компилятору «доверься мне», хотя компилятор может быть прав, а разработчик — нет.- Discriminated union без exhaustive check — ситуация, когда добавляется новый вариант в union, а обработку в switch забыли добавить, и TypeScript это не подсвечивает, если явно не проверять полноту через
never.
Отдельно разобрали, почему вместо any лучше использовать unknown: unknown не даёт напрямую вызывать методы или обращаться к полям без предварительной проверки типа (type guard). Кандидат перечислил основные type guards: typeof, instanceof, проверку свойства через in, а также проверки через классы и прототипы.
Дженерики кандидат честно назвал своей слабой темой, но привёл рабочий пример — интерфейс пагинации с generic-полем items, который позволяет использовать одну структуру и для массива пользователей, и для массива продуктов, не дублируя код.
Взаимодействие фронтенда с бэкендом
Здесь вопросы уже не про «знаешь ли ты React», а про то, как ты реально организуешь работу с API в проекте.
- URL бэкенда хранится через переменные окружения (
.env) — базовая гигиена, но спрашивают не зря: удивительно часто встречаются захардкоженные адреса в коде. - Кандидат использовал паттерн «papka servis» отдельно от FSD-архитектуры для организации API-запросов — то есть слой сервисов вынесен отдельно от фичей и сущностей.
- Инструмент — TanStack Query, с флагами
enabled,isLoading,isFetchingдля управления состоянием загрузки и условными запросами. - Обсуждали Next.js: серверные и клиентские компоненты. Базовое определение кандидат дал верно — серверные рендерятся на сервере, клиентские на клиенте. А вот сложный кейс — синхронизация серверных данных с TanStack Query через infinite scroll, с использованием параметра
initialData— вызвал затруднение. Кандидат честно признался, что почти не работал с этим параметром на практике.
Это нормальная картина для стажёрского уровня: база закрыта, а продвинутые кейсы синхронизации SSR-данных с клиентским кешированием — это уже следующая ступень.
NestJS и работа с бэкендом
- Guards — использовались для проверки ролей пользователя, что-то вроде RBAC-подхода (role-based access control).
- Interceptors — применялись для перехвата запросов со статусом 401 и обработки логики повторной авторизации.
- Декораторы — использовались повсеместно, они и есть значительная часть архитектуры NestJS.
- Способы взаимодействия клиент-сервер: HTTP/REST — на практике, GraphQL и gRPC — только в теории, без реального опыта применения.
- Prisma: назначение ORM объяснено верно — упрощение работы с SQL, миграции, типизация моделей базы данных. Миграции нужны, чтобы отслеживать изменения схемы БД в командной разработке и не ловить рассинхронизацию между разработчиками, которые параллельно меняют структуру таблиц.
Live coding: где кандидат просел
Первое задание было небольшим и концептуальным: почему при двух подряд вызовах setCount(count + 1) счётчик увеличивается только на 1, а не на 2. Кандидат правильно определил суть проблемы — замыкание захватывает устаревшее значение count на момент рендера — и предложил решение через функциональное обновление состояния (setCount(prev => prev + 1)). Чистый плюс, отвечает быстро и по делу.
А вот дальше начинается самая показательная часть всего интервью — алгоритмическая задача на поиск пересечения двух массивов без дубликатов.
Путь кандидата к решению:
- Первая идея — вложенный цикл, то есть решение за O(n²). Рабочее, но неоптимальное.
- После нескольких прямых подсказок интервьюера кандидат переходит к идее использовать словарь (Map): сначала заполнить его элементами первого массива, затем пройтись по второму и проверять наличие элемента в словаре.
- По пути — целая серия мелких ошибок: путаница с индексами, лишние условия, неправильная проверка дубликатов через
includesна результирующем массиве вместо проверки самого словаря. - Итоговое рабочее решение получено только благодаря нескольким прямым подсказкам, а не самостоятельно.
- Интервьюер в финале показал более элегантный вариант — через
SetвместоMap. По производительности идентично, но короче кода и уникальность значений гарантируется структурой данных сама по себе, без ручных проверок.
Важный нюанс: интервьюер прямо говорит, что не ставит алгоритмические задачи в приоритет при итоговой оценке, потому что такую задачу можно решить через ChatGPT за десять секунд. Гораздо важнее ход мысли и честность попытки — видно ли, что человек реально думает, а не подглядывает в телефон под столом. При этом отмечается: кандидата легко «спалить» на использовании ChatGPT прямо во время звонка — по характерным паузам, панике при смене формулировки вопроса и специфическому звуку набора текста.
Итоговая оценка и что это значит для вас
Финальный вердикт интервьюера: сильные знания React и TypeScript, впечатляющий практический опыт для уровня стажёра — включая работу с микросервисами, что для junior-позиции скорее редкость. При этом явный провал по алгоритмической задаче — кандидат не дошёл до рабочего решения даже с явными подсказками.
И тут интервьюер сам ставит интересный вопрос: насколько вообще актуален формат классического технического собеседования на фоне развития ИИ-инструментов. Если алгоритмическую задачу можно решить через ChatGPT за минуту, а архитектурные решения в реальных проектах кандидат принимал сам — что важнее оценивать на входе?
Что делать прямо сейчас: чек-лист подготовки
Если вы готовитесь к похожему собеседованию, вот конкретный список, который стоит закрыть за одну-две недели:
- Соберите один законченный пет-проект с нетривиальной частью — авторизация с ротацией токенов, микросервисы, любая асинхронная логика с реальными подводными камнями. Один доведённый проект сильнее пяти брошенных.
- Прогоните механику ре-рендеров React руками: напишите компонент с
React.memo, добавьте дочерний компонент с функцией-пропсом, проверьте разницу сuseCallbackи без него в React DevTools Profiler. - Разберите разницу между
OmitиExclude,PickиExtract— на живых примерах в редакторе, а не по памяти. Пропишите руками по 2-3 варианта использования каждого. - Проговорите вслух, зачем нужен
unknownвместоany, и приведите пример type guard для каждого способа:typeof,instanceof,in. - Порешайте 10-15 простых задач на массивы и строки с акцентом на Map/Set — не ради запоминания решений, а чтобы не путаться в базовой логике под давлением. Особое внимание — на разницу производительности O(n²) vs O(n) и на то, когда
SetудобнееMap. - Подготовьте историю про самую сложную техническую проблему, которую вы решали — с деталями, а не общими словами. Именно такие истории интервьюеры запоминают лучше всего.
- Не полагайтесь на ChatGPT во время звонка — если задача рабочая, ход мысли важнее финального кода, а попытку скрыть подглядывание распознают быстро.
Что мы заметили
Разбирая это собеседование, мы обратили внимание на несколько закономерностей, которые встречаются в свежих материалах по найму фронтенд-разработчиков в целом.
Первое — почти везде приоритет смещается с алгоритмических задач в сторону практического опыта и архитектурных решений. Формат «два часа лайвкодинга на LeetCode» постепенно уступает место разбору реальных пет-проектов и обсуждению конкретных технических решений в них.
Второе — есть расхождение в подходах к оценке кандидата. Более мягкий подход (как в этом собеседовании) допускает провал по алгоритмам, если остальная база сильная — теория, архитектурное мышление, честность в ответах. Более консервативный взгляд, который встречается в других разборах интервью, всё же считает алгоритмическую секцию отсекающей: не решил задачу — не прошёл, независимо от остальных ответов. Какой подход правильный — зависит от команды и от того, что реально будет делать стажёр в первые месяцы: писать бизнес-логику или оптимизировать highload-сервисы.
Третье — тема ИИ-инструментов на собеседованиях становится центральной, а не побочной. Раньше вопрос был «списывает кандидат или нет», сейчас вопрос смещается в сторону «а что вообще имеет смысл проверять, если решение задачи занимает у ChatGPT десять секунд». Это меняет саму структуру интервью: меньше внимания финальному коду, больше — процессу мышления и умению объяснить архитектурные решения своими словами.
Четвёртое — структурная типизация TypeScript и разница между Omit/Exclude всплывают в разборах постоянно. Похоже, это одна из самых частых точек путаницы даже у разработчиков с реальным продакшн-опытом, а не только у новичков.
Обобщение
Собеседование в разборе — не идеальный кандидат и не провальный, а вполне рабочий средний случай: сильная теория, реальный практический опыт с непростыми штуками вроде микросервисов и токен-ротации, и совершенно ожидаемая просадка на классической алгоритмике под стрессом живого звонка. Именно такие интервью и стоит разбирать — не выдуманные идеальные ответы из статей, а живой процесс с ошибками, подсказками и честными «я не знаю».
Если вы готовитесь к похожему собеседованию на позицию фронтенд-стажёра или fullstack-джуниора — не тратьте всю подготовку на LeetCode. Разберите один свой проект до мелочей, закройте пробелы в TypeScript-утилитах и механике React-рендеров, и потренируйтесь объяснять ход мысли вслух — именно это, судя по всему, сейчас решает больше, чем идеально написанный алгоритм за 20 минут.