Вопросы для собеседования fullstack-разработчика
Готовый набор для интервьюера: 36 вопросов с тезисами эталонного ответа, задача, рубрика и план встречи.
На собеседовании fullstack-разработчика хорошо работает схема на 60 минут: 3-4 устных вопроса по стеку, одна задача на live coding, проектирование для middle и выше и вопросы кандидата. Ниже вопросы по грейдам из базы Техпрогона: у каждого тезисы, которые должен назвать сильный кандидат.
Всего в базе по этой роли 499 устных вопросов с чек-листом ответа.
Как построить техсобес на 60 минут
0-5 мин
Знакомство
Кто вы, формат встречи, кандидат коротко о себе и последнем проекте.
5-20 мин
Вопросы по стеку
3-4 устных вопроса из набора ниже. Сверяйте ответ с тезисами.
20-45 мин
Live coding
Одна задача. Смотрите на процесс по рубрике, а не только на ответ.
45-55 мин
Проектирование
Для middle и выше: система или решение целиком. Для junior: ещё вопросы.
55-60 мин
Вопросы кандидата
Что спросил кандидат, тоже сигнал: интерес к задачам и команде.
Junior: 12 вопросов
Основы: как устроено и зачем. Хватит 3-4 вопросов на встречу.
Чем отличаются var, let и const? Что из них вы используете и почему?
Что хотим услышать
varс функциональной областью,letиconstс блочнойvarподнимается сundefined,let/constпопадают в TDZvarможно объявить повторно,let/constнетconstзапрещает переприсваивание, но не изменение объектаvarна верхнем уровне скрипта становится свойствомwindow- По умолчанию
const,letдля изменяемых,varне использую
Если ответил, копните глубже
let в цикле for ведёт себя иначе, чем var?let?Почему any считают вредным и чем его заменить?
Что хотим услышать
anyвыключает проверку в обе стороны: на нём можно вызвать что угодно и присвоить его куда угодноanyзаразен: свойства и результаты вызовов на нём тожеany, ошибка уходит далеко от источника- Замена для неизвестных данных:
unknownс сужением или валидацией - Замена для «любого, но одного и того же»: дженерик
- Защита:
noImplicitAnyв strict и правило линтераno-explicit-any
Если ответил, копните глубже
any появляется неявно?any всё же оправдан?Как работает useEffect: когда запускается, зачем массив зависимостей и функция возврата?
Что хотим услышать
- эффект запускается после коммита и отрисовки
- пустой массив - один раз при монтировании
- без массива - после каждого рендера
- возвращённая функция - очистка, вызывается перед следующим запуском и при размонтировании
- зависимости сравниваются поверхностно через Object.is
- в StrictMode в разработке эффект намеренно запускается дважды
Если ответил, копните глубже
Объясни разницу между textContent, innerText и innerHTML. Что из этого и когда использовать?
Что хотим услышать
textContentвозвращает весь текст, включая скрытый, без учёта CSSinnerTextучитывает стили, возвращает видимый текст и при чтении вызывает пересчёт layoutinnerHTMLчитает и пишет разметку, присваивание парсит HTML- Пользовательские данные через
innerHTMLвставлять нельзя: это XSS - Перезапись
innerHTMLпересоздаёт потомков и теряет обработчики и состояние полей - По умолчанию для текста выбирать
textContent
Если ответил, копните глубже
innerText медленнее textContent?insertAdjacentHTML и чем он лучше innerHTML +=?Чем отличаются display: inline, block и inline-block? Приведи пример, когда нужен каждый.
Что хотим услышать
- block занимает всю доступную ширину и начинается с новой строки
- inline течёт в строке, игнорирует width и height
- У inline вертикальные margin не сдвигают соседей
- inline-block стоит в строке, но принимает размеры и отступы как блок
- Примеры:
<p>и<div>блоки,<a>и<span>строчные, кнопка-тег inline-block - Сегодня для раскладки в ряд чаще берут flex, а не inline-block
Если ответил, копните глубже
display: contents?<img> по умолчанию?Объясни, что такое CORS и как он работает.
Что хотим услышать
- Same-origin policy: скрипт не может читать ответы другого origin (схема, хост, порт)
- CORS: сервер заголовками
Access-Control-Allow-*разрешает конкретным origin читать ответы - Проверяет браузер, сервер отвечает всегда; вне браузера CORS не действует
- Простые запросы уходят сразу, остальные после preflight OPTIONS
- С куками нужен
credentials: 'include',Allow-Credentials: trueи точный origin вместо*
Если ответил, копните глубже
Объясни, как ты оцениваешь временную и пространственную сложность своего решения на собесе.
Что хотим услышать
- Называет, что такое n (и m, если входов несколько)
- Считает циклы: вложенные перемножаются, последовательные складываются
- Помнит скрытые циклы: includes, indexOf, slice, sort, spread
- Отдельно оценивает память, включая стек рекурсии
- Сравнивает с ограничениями: при n до 10⁵ O(n²) уже не годится
Если ответил, копните глубже
arr.slice() внутри цикла?Перед тобой код на ревью. Скажи вслух, что здесь не так - по убыванию важности. Читай вслух, как на ревью: сначала что делает код, потом что сломано.
Что хотим услышать
- debounce создаётся заново на каждом рендере - откладывание не работает
- нужен useMemo или useRef, чтобы функция пережила рендер
- нет отмены отложенного вызова при размонтировании
- q не экранирован через encodeURIComponent
- нет отмены предыдущего запроса - остаётся гонка
- проще перенести задержку в эффект с зависимостью от value
Если ответил, копните глубже
Объясни, зачем фронтенд-проекту сборщик и что происходит с кодом от исходников до файлов в dist.
Что хотим услышать
- граф зависимостей от точки входа
- трансформация: TS, JSX, современный синтаксис, CSS
- резолв пакетов из node_modules
- склейка в чанки, минификация, tree shaking
- хеши в именах для долгого кеша
- dev-сервер с HMR и source maps
Если ответил, копните глубже
Расскажи, как эволюционировала асинхронность в Node.js: колбэки, промисы, async/await. Что под капотом у await?
Что хотим услышать
- Колбэки error-first: просто, но вложенность и легко потерять ошибку
- Промисы: цепочки, единая обработка ошибок, комбинаторы all/allSettled/race/any
- async/await: синтаксис над промисами, код читается как синхронный, try/catch
- await приостанавливает только свою функцию, продолжение идёт микрозадачей
- async-функция всегда возвращает промис
- util.promisify и промис-версии модулей (fs/promises)
Если ответил, копните глубже
Чем отличается WHERE от HAVING? Приведи пример, где нужны оба.
Что хотим услышать
- WHERE фильтрует строки до группировки, HAVING фильтрует группы после
- В WHERE нельзя агрегаты, в HAVING можно
- Порядок: FROM, WHERE, GROUP BY, HAVING, SELECT
- Условия без агрегатов лучше писать в WHERE: меньше строк до группировки
- Пример: WHERE status = 'paid' и HAVING SUM(total) > 10000
Если ответил, копните глубже
Объясните разницу между вертикальным и горизонтальным масштабированием и когда что выбирать.
Что хотим услышать
- Вертикальное: мощнее машина, код не меняется, есть предел и единая точка отказа
- Горизонтальное: больше узлов, нужен stateless или распределённое состояние
- Stateless-сервисы приложения масштабируют горизонтально в первую очередь
- БД часто сначала масштабируют вертикально и репликами, шардирование позже
- Горизонтальное даёт отказоустойчивость, но добавляет сложность
Если ответил, копните глубже
Middle: 12 вопросов
Практика: почему так, какие компромиссы, что ломается.
Объясни event loop: как браузер решает, что выполнять следующим.
Что хотим услышать
- JS однопоточный, есть стек вызовов
- макрозадачи: таймеры, события, сеть - по одной за цикл
- микрозадачи: промисы, queueMicrotask, MutationObserver
- после каждой макрозадачи очередь микрозадач вычерпывается полностью
- рендер происходит между макрозадачами, requestAnimationFrame - перед отрисовкой
- бесконечная генерация микрозадач заблокирует рендер
Если ответил, копните глубже
Напишите Pick и Omit своими словами и объясните, как они работают.
Что хотим услышать
Pick<T, K extends keyof T> = { [P in K]: T[P] }: mapped-тип по выбранным ключам- Ограничение
K extends keyof Tзапрещает несуществующие ключи Omit<T, K> = Pick<T, Exclude<keyof T, K>>: оставить все ключи, кроме указанных- У встроенного
Omitключ не ограниченkeyof T, поэтому опечатка проходит молча - Оба не распределяются по объединению и работают на верхнем уровне
Если ответил, копните глубже
Omit строгим?Omit для объединения?Pick сохраняет readonly и ??Как работает контекст и почему из-за него всё перерисовывается?
Что хотим услышать
- все потребители перерисовываются при смене значения провайдера
- сравнение идёт по ссылке на value
- объект-литерал в value меняет ссылку каждый рендер
- лечение: useMemo на value и разделение контекстов
- контекст - про передачу, а не про управление состоянием
- memo не спасает потребителя контекста
Если ответил, копните глубже
Тебе говорят: «страница тормозит». Как ты будешь искать и устранять проблему?
Что хотим услышать
- Уточнить, что именно медленно: загрузка, отклик на действия или прыжки вёрстки
- Посмотреть полевые данные (CrUX, web-vitals в аналитике) и лабораторные (Lighthouse, Performance)
- Для LCP: найти LCP-элемент, проверить TTFB, приоритет загрузки, блокирующие ресурсы
- Для INP: найти длинные задачи в профиле, разбить работу, вынести в воркер, уменьшить JS
- Для CLS: размеры у медиа, резерв места, шрифты
- После правки измерить снова и следить за метриками в мониторинге
Если ответил, копните глубже
Как бы ты сделал доступное модальное окно? Что должно происходить с фокусом и клавиатурой?
Что хотим услышать
- При открытии фокус переходит внутрь окна (на заголовок или первое поле)
- Фокус не уходит на фон: ловушка фокуса или инертный фон
- Escape закрывает окно
- После закрытия фокус возвращается на кнопку, которая открыла окно
- Роль dialog,
aria-modal="true"и имя черезaria-labelledbyна заголовок - Нативный
<dialog>сshowModal()даёт большую часть этого из коробки
Если ответил, копните глубже
Чем отличаются сессии на куках и JWT? Что бы ты выбрал для обычного веб-приложения?
Что хотим услышать
- Сессия: id в куке, состояние на сервере (Redis/БД), отзыв мгновенный
- JWT: состояние в самом токене, проверка подписью без базы, сложно отозвать
- Для JWT нужна пара короткий access + отзываемый refresh
- Хранение: HttpOnly кука защищает от XSS, но нужна защита от CSRF (SameSite)
- Выбор по контексту: одно веб-приложение проще на сессиях, много сервисов и мобильные клиенты удобнее на токенах
Если ответил, копните глубже
Объясни решение задачи «минимальное число монет для суммы» и его сложность. Почему тут не подходит жадность?
Что хотим услышать
- Показывает контрпример для жадности
- Формулирует состояние: dp[s] минимум монет для суммы s
- Переход: dp[s] = min(dp[s - c] + 1) по монетам
- База dp[0] = 0, остальные Infinity, недостижимо → -1
- O(S · k) времени, O(S) памяти
Если ответил, копните глубже
Сравни CSR, SSR, SSG и ISR. Когда что выберешь?
Что хотим услышать
- CSR: пустой HTML, всё в браузере, дёшево, плохо для SEO и первого экрана; для кабинетов
- SSR: HTML на каждый запрос, свежие данные и SEO, нагрузка на сервер и TTFB
- SSG: HTML при сборке, CDN, самый быстрый, данные устаревают
- ISR: статика с фоновой пересборкой, компромисс скорости и свежести
- Гидрация нужна после SSR и SSG, до неё страница не интерактивна
- Пример выбора по типам страниц одного продукта
Если ответил, копните глубже
Как бы ты построил CI/CD для веб-приложения с нуля?
Что хотим услышать
- На каждый PR: npm ci, линтер, типы, unit-тесты, сборка
- Быстрые проверки первыми, независимые джобы параллельно, кеш зависимостей
- Защита ветки main: мерж только с зелёным CI и ревью
- Сборка один раз, артефакт (образ) с версией продвигается по стендам
- Деплой на стенд автоматически, на прод с подтверждением или автоматически при хорошем покрытии
- План отката и миграции, совместимые с предыдущей версией
Если ответил, копните глубже
Каталог товаров тормозит под нагрузкой. Как бы ты добавил кеширование и какие проблемы учёл?
Что хотим услышать
- Сначала измерить: какие запросы горячие, доля чтений и записей
- Cache-aside в Redis с TTL, ключи с понятной схемой (product:42)
- Инвалидация при изменении: удалять ключ, учитывать производные ключи (списки)
- Cache stampede: блокировка на пересчёт, объединение запросов, jitter к TTL
- Лимит памяти и политика вытеснения allkeys-lru
- Метрики: hit rate, задержка; кеш не должен быть единственным источником правды
Если ответил, копните глубже
Спроектируй схему базы для заказов интернет-магазина: клиенты, товары, заказы, оплаты. Какие таблицы, ключи и ограничения?
Что хотим услышать
- Таблицы customers, products, orders, order_items, payments и их связи
- order_items как связь заказа и товара с qty и ценой на момент покупки
- Ограничения: FK, NOT NULL, CHECK (qty > 0), UNIQUE там, где нужно
- Типы: numeric или копейки для денег, timestamptz для времени, статус через CHECK или enum
- Индексы на FK и под частые запросы (заказы клиента по дате)
- Платежи отдельной таблицей с идемпотентным внешним id от платёжки
Если ответил, копните глубже
Расскажите, по какому плану вы будете вести секцию system design на 60 минут.
Что хотим услышать
- Начинаю с уточнения функциональных требований и явно фиксирую, что не делаем
- Уточняю нефункциональные требования: масштаб, задержка, доступность, консистентность
- Делаю грубую оценку RPS и объёмов и использую её в решениях
- Описываю API и модель данных, выбираю хранилище под паттерн доступа
- Рисую высокоуровневую схему и прохожу по ней путь запроса
- Оставляю время на узкие места, отказы, масштабирование и мониторинг
Если ответил, копните глубже
Senior: 12 вопросов
Глубина и опыт: границы подходов, решения в продакшене.
Как бы вы реализовали перебор всех записей постраничного API так, чтобы вызывающий код просто писал for await (const item of allItems())?
Что хотим услышать
async function*, внутри цикл по страницам сawait fetchyield*по элементам страницы илиyieldпоштучно- Условие остановки: нет курсора
nextили пустая страница - Ленивость: следующая страница грузится только когда потребитель дошёл до конца текущей
- Досрочный
breakу потребителя завершает генератор, запросы прекращаются;finallyдля очистки - Ошибки и отмена:
try/catchу потребителя,AbortSignalв параметрах
Если ответил, копните глубже
Объясните, как условный тип ведёт себя с объединением и когда это мешает.
Что хотим услышать
- Голый параметр в
T extends Uдаёт распределение по членам объединения - На этом построены
Exclude,Extract,NonNullable neverкак пустое объединение даётneverв результате- Отключение:
[T] extends [U], нужно дляIsNeverи проверок всего объединения - Распределение срабатывает только для параметра типа, а не для конкретного объединения, записанного прямо в условии
Если ответил, копните глубже
Exclude работает поэлементно?boolean extends true ? 1 : 2?Как бы ты загрузил данные в компоненте через useEffect, чтобы это было корректно? И почему в реальном проекте ты, возможно, сделал бы иначе?
Что хотим услышать
- Зависимости: всё, от чего зависит запрос, например id
- Гонки: в очистке AbortController или флаг ignore
- Состояния загрузки и ошибки, AbortError не считать ошибкой
- StrictMode в dev отправит запрос дважды, это нормально при корректной очистке
- Нет кеша, дедупликации, повторов и предзагрузки: каждый компонент грузит сам, возникают водопады
- В проекте: TanStack Query, SWR, загрузчики роутера или серверные компоненты
Если ответил, копните глубже
Объясни, из чего складывается INP и как ты его улучшал бы в большом SPA.
Что хотим услышать
- INP складывается из задержки ввода, времени обработчиков и задержки отрисовки
- Задержку ввода создают длинные задачи, которые уже идут в момент клика
- Разбивать работу и уступать главный поток: scheduler.yield или setTimeout
- Сначала дать визуальный отклик, тяжёлое делать после
- Сокращать JS, гидрацию и ререндеры: код-сплиттинг, startTransition, мемоизация, виртуализация
- Мерить в поле с атрибуцией, чтобы знать конкретные медленные взаимодействия
Если ответил, копните глубже
Как бы ты сделал надёжный клиент WebSocket для чата, который переживает обрывы сети?
Что хотим услышать
- Реконнект с экспоненциальной задержкой и джиттером, сброс задержки после успеха
- Heartbeat: ping/pong с таймаутом, при пропуске закрыть и переподключиться
- Нумерация сообщений: после реконнекта клиент сообщает последний id, сервер досылает пропущенное
- Очередь исходящих сообщений с клиентскими id, идемпотентность на сервере против дублей
- Реакция на
online/offlineи видимость вкладки, повторная авторизация при истёкшем токене
Если ответил, копните глубже
Спроектируй веб-клиент мессенджера как в Telegram Web: список чатов, переписка, отправка текста и фото, статусы «отправлено/прочитано», «печатает». Пройди этапы: требования, оценка нагрузки, API, данные, компоненты, узкие места.
Что хотим услышать
- Требования: список чатов, история, отправка, статусы, «печатает», уведомления, офлайн и плохая сеть
- Оценка: тысячи чатов, длинная история, в групповом чате десятки сообщений в секунду, долгоживущая вкладка
- API: WebSocket для событий (message.new, typing, read), REST для истории с курсором вверх, загрузка файлов отдельно
- Данные: сообщения по чатам с клиентским id, нормализация, IndexedDB для кеша и офлайн-очереди
- Компоненты: ChatList, ChatWindow с виртуализацией в обратном порядке, Composer, MessageBubble
- Узкие места: порядок и дедупликация, переподключение с догрузкой пропущенного, батчинг обновлений, утечки памяти, позиция скролла при подгрузке истории
Если ответил, копните глубже
Ты приходишь в проект без тестов. Как выстроишь стратегию тестирования?
Что хотим услышать
- начать с самого рискованного и ценного для бизнеса: оплата, вход, ключевые сценарии
- несколько e2e smoke-тестов на критичные пути
- unit-тесты на чистую логику: расчёты, форматирование, утилиты
- интеграционные тесты компонентов и API на новых и изменяемых местах
- тесты в CI как обязательная проверка PR
- регрессионный тест на каждый найденный баг, борьба с флаки
Если ответил, копните глубже
Расскажите, через что проходит HTTP-запрос в NestJS от входа до ответа и где что уместно делать.
Что хотим услышать
- Middleware: низкоуровневое, до роутинга (логирование, cors, raw body), как в Express
- Guards: решение «пускать или нет» (аутентификация, роли), имеют
ExecutionContextи метаданные хендлера - Interceptors до хендлера и после: трансформация ответа, тайминги, кеш, таймауты (RxJS вокруг вызова)
- Pipes: валидация и преобразование аргументов (
ValidationPipe,ParseIntPipe) - Хендлер контроллера вызывает сервисы; исключения ловят Exception filters и превращают в ответ
- Порядок уровней: глобальные, потом контроллера, потом метода
Если ответил, копните глубже
Расскажите, как в PostgreSQL устроен MVCC, откуда берутся мёртвые строки и зачем нужен VACUUM.
Что хотим услышать
UPDATEсоздаёт новую версию строки, старая остаётся сxmax;DELETEтолько помечает версию- Каждая транзакция видит версии по своему снимку, поэтому чтение не блокирует запись и наоборот
- VACUUM удаляет версии, которые уже никому не видны, обновляет visibility map и замораживает старые строки
- Долгие транзакции и забытые слоты репликации мешают очистке и раздувают таблицы
- Autovacuum настраивают по таблицам:
autovacuum_vacuum_scale_factor, лимиты стоимости; мониторинг черезpg_stat_user_tables - HOT-обновления и
fillfactorуменьшают нагрузку на индексы
Если ответил, копните глубже
VACUUM FULL отличается от обычного?Спроектируйте мессенджер с личными и групповыми чатами, доставкой в реальном времени и историей.
Что хотим услышать
- Требования: личные и групповые чаты до 1 000 участников, доставка онлайн и офлайн (push), история, статусы «доставлено» и «прочитано»; порядок сообщений в чате; медиа и сквозное шифрование вне рамок
- Оценка нагрузки: 50 млн DAU по 40 сообщений ≈ 2 млрд в сутки ≈ 20 000 RPS записи, в пике ~60 000; 10 млн одновременных соединений, ~100 000 на gateway → ~100 gateway; 2 млрд × 200 байт ≈ 400 ГБ в сутки, ~150 ТБ в год без реплик
- API: WebSocket: send {chat_id, client_msg_id, text}, ack {msg_id}, события new_message, read; HTTP: GET /chats/{id}/messages?before=...
- Данные: чаты и участники в PostgreSQL, сообщения в Cassandra/ScyllaDB с ключом партиции chat_id и сортировкой по msg_id, реестр user_id → gateway в Redis с TTL
- Компоненты: gateway-серверы с соединениями, сервис сообщений (сохранить, присвоить id, разослать), сервис присутствия, push-сервис для офлайн, очередь для фан-аута в группах
- Узкие места: большие группы (фан-аут через очередь), дубли при переотправке (client_msg_id для идемпотентности), переподключения и догрузка пропущенного, горячие чаты, падение gateway
Если ответил, копните глубже
Пользователи жалуются, что https://api.example.com не открывается. Как будешь искать причину?
Что хотим услышать
- Воспроизвести и уточнить симптом: таймаут, refused, ошибка TLS, 5xx
- DNS:
dig +short, правильный ли IP, не истёк ли домен - Сеть и порт снаружи:
curl -v,nc -zv host 443; refused или timeout - На сервере: слушает ли процесс и на каком адресе (
ss -tlnp), отвечает ли локально - Фаервол на хосте и в облаке, балансировщик и его health checks
- Логи прокси и приложения, сертификат и его срок
Если ответил, копните глубже
Вы претендуете на senior. Расскажите о себе.
Что хотим услышать
- Назван масштаб: команда, нагрузка, пользователи или деньги
- Есть решения, которые повлияли дальше своего кода
- Есть метрики результата
- Упомянуто влияние на команду: менторинг, процессы, ревью
- Уложился в 2 минуты, не утонул в деталях
Если ответил, копните глубже
Задача для live coding
Отменяемая операция
Напиши makeCancelable(promise): вернуть {promise, cancel}. После cancel() итоговый промис отклоняется объектом {canceled: true}, а исходный результат игнорируется.
Промис неотменяем по спецификации: мы отменяем не работу, а подписку. Флаг canceled + проверка в обоих обработчиках. Настоящая отмена сетевого запроса - это AbortController и signal, проговори это вслух как follow-up.
- Подсказка, если застрял: Промис нельзя отменить - можно только перестать реагировать на его результат.
- Подсказка, если застрял: Флаг в замыкании проверяется в обоих колбэках.
Эталон, тесты и таймер открываются интервьюеру в живом моке
Рубрика оценки: 1-4 балла
Те же критерии процесса, что в живом моке Техпрогона. Ставьте балл по каждому пункту, сумма показывает картину лучше, чем «понравился или нет».
Уточнил условие
Ограничения, формат входа, что вернуть при пустом вводе
Привёл примеры до кода
Обычный случай и хотя бы один краевой
Проговорил алгоритм словами
До того как писать код
Назвал сложность
По времени и по памяти
Написал рабочий код
Тесты прошли
Сам проверил краевые случаи
Прогнал в голове пустой ввод, один элемент, дубликаты
Нашёл и исправил ошибку сам
Или ошибок не было с первого раза
Мок-собес без регистрации для интервьюера
Хотите проверить кандидата на задаче с эталоном и рубрикой? Так работает живой мок в Техпрогоне.
Кандидат готовится в Техпрогоне и на задаче открывает ступень «Живой мок».
Он присылает вам ссылку. Вы открываете её без регистрации.
У вас условие, эталон, рубрика, уточняющие вопросы и таймер.
Вы ставите оценки и вердикт, кандидат сразу видит результат.
Для интервьюера бесплатно. Ссылку создаёт кандидат на тарифе Рывок
Наборы для других ролей
Готовитесь к собесу сами? Подготовка к собеседованию: Fullstack на JS/TS