Вопросы для собеседования C#/.NET-разработчика
Готовый набор для интервьюера: 36 вопросов с тезисами эталонного ответа, задача, рубрика и план встречи.
На собеседовании C#/.NET-разработчика хорошо работает схема на 60 минут: 3-4 устных вопроса по стеку, одна задача на live coding, проектирование для middle и выше и вопросы кандидата. Ниже вопросы по грейдам из базы Техпрогона: у каждого тезисы, которые должен назвать сильный кандидат.
Всего в базе по этой роли 201 устных вопросов с чек-листом ответа.
Как построить техсобес на 60 минут
0-5 мин
Знакомство
Кто вы, формат встречи, кандидат коротко о себе и последнем проекте.
5-20 мин
Вопросы по стеку
3-4 устных вопроса из набора ниже. Сверяйте ответ с тезисами.
20-45 мин
Live coding
Одна задача. Смотрите на процесс по рубрике, а не только на ответ.
45-55 мин
Проектирование
Для middle и выше: система или решение целиком. Для junior: ещё вопросы.
55-60 мин
Вопросы кандидата
Что спросил кандидат, тоже сигнал: интерес к задачам и команде.
Junior: 12 вопросов
Основы: как устроено и зачем. Хватит 3-4 вопросов на встречу.
Расскажите, что такое стек и куча в .NET и что где хранится.
Что хотим услышать
- Стек: кадры вызовов методов, локальные переменные и параметры, освобождается автоматически при выходе
- Куча: объекты ссылочных типов, ими управляет сборщик мусора
- Тип значения хранится там, где объявлена переменная: поле класса лежит в куче внутри объекта
- Упаковка переносит копию значения в кучу
- Локальные переменные в лямбдах и async-методах переезжают в объекты в куче (замыкания, машина состояний)
Если ответил, копните глубже
Чем отличается WHERE от HAVING? Приведи пример, где нужны оба.
Что хотим услышать
- WHERE фильтрует строки до группировки, HAVING фильтрует группы после
- В WHERE нельзя агрегаты, в HAVING можно
- Порядок: FROM, WHERE, GROUP BY, HAVING, SELECT
- Условия без агрегатов лучше писать в WHERE: меньше строк до группировки
- Пример: WHERE status = 'paid' и HAVING SUM(total) > 10000
Если ответил, копните глубже
Объясни, что такое CORS и как он работает.
Что хотим услышать
- Same-origin policy: скрипт не может читать ответы другого origin (схема, хост, порт)
- CORS: сервер заголовками
Access-Control-Allow-*разрешает конкретным origin читать ответы - Проверяет браузер, сервер отвечает всегда; вне браузера CORS не действует
- Простые запросы уходят сразу, остальные после preflight OPTIONS
- С куками нужен
credentials: 'include',Allow-Credentials: trueи точный origin вместо*
Если ответил, копните глубже
Объясните разницу между вертикальным и горизонтальным масштабированием и когда что выбирать.
Что хотим услышать
- Вертикальное: мощнее машина, код не меняется, есть предел и единая точка отказа
- Горизонтальное: больше узлов, нужен stateless или распределённое состояние
- Stateless-сервисы приложения масштабируют горизонтально в первую очередь
- БД часто сначала масштабируют вертикально и репликами, шардирование позже
- Горизонтальное даёт отказоустойчивость, но добавляет сложность
Если ответил, копните глубже
Объясни, как ты оцениваешь временную и пространственную сложность своего решения на собесе.
Что хотим услышать
- Называет, что такое n (и m, если входов несколько)
- Считает циклы: вложенные перемножаются, последовательные складываются
- Помнит скрытые циклы: includes, indexOf, slice, sort, spread
- Отдельно оценивает память, включая стек рекурсии
- Сравнивает с ограничениями: при n до 10⁵ O(n²) уже не годится
Если ответил, копните глубже
arr.slice() внутри цикла?Объясни, зачем фронтенд-проекту сборщик и что происходит с кодом от исходников до файлов в dist.
Что хотим услышать
- граф зависимостей от точки входа
- трансформация: TS, JSX, современный синтаксис, CSS
- резолв пакетов из node_modules
- склейка в чанки, минификация, tree shaking
- хеши в именах для долгого кеша
- dev-сервер с HMR и source maps
Если ответил, копните глубже
Назовите ваши сильные стороны.
Что хотим услышать
- Две-три стороны, а не список из десяти
- Каждая подкреплена примером
- Стороны связаны с вакансией
- Нет пустых слов вроде «стрессоустойчивый, коммуникабельный» без доказательств
- Уверенно, без хвастовства
Если ответил, копните глубже
Расскажите, как гарантированно освободить ресурс в C, даже при исключении.
Что хотим услышать
- Блок finally выполняется при любом выходе из try
- using разворачивается в try/finally с вызовом Dispose
- using-объявление (C# 8): using var до конца области видимости
- await using и IAsyncDisposable для асинхронного освобождения
- finally не выполнится при завершении процесса и StackOverflow
Если ответил, копните глубже
Расскажи, какие бывают JOIN и как каждый влияет на число строк в результате.
Что хотим услышать
- INNER: только пары, где условие ON истинно
- LEFT/RIGHT: все строки одной стороны, у недостающей стороны NULL
- FULL: все строки обеих сторон
- CROSS: декартово произведение, N × M строк
- Связь один ко многим размножает строки «одной» стороны, два таких JOIN перемножают
- Строки с NULL в ключе не соединяются: NULL = NULL не true
Если ответил, копните глубже
Чем отличаются GET и POST? Ответ «у POST есть тело» не засчитывается.
Что хотим услышать
- GET читает и безопасен, POST меняет состояние
- GET идемпотентен, POST нет: его нельзя молча повторять
- GET кешируется браузером и прокси, POST по умолчанию нет
- Параметры GET в URL: попадают в логи и историю, длина ограничена серверами
- GET можно добавить в закладки и prefetch, поэтому нельзя менять данные по GET
Если ответил, копните глубже
Зачем в системе очередь сообщений? Приведите примеры.
Что хотим услышать
- Асинхронность: пользователь не ждёт долгой работы
- Сглаживание пиков: очередь копит, консьюмеры разбирают
- Развязка сервисов: продюсер не знает о получателях
- Ретраи при временных сбоях получателя
- Цена: eventual consistency, дубли, мониторинг отставания
Если ответил, копните глубже
Объясни подход к задаче «найти два числа с суммой target» и его сложность.
Что хотим услышать
- Сначала называет перебор O(n²) как базовое решение
- Идея: для x ищем дополнение target - x в Map
- Проверяет Map до вставки x, чтобы не взять один элемент дважды
- Сложность O(n) времени и O(n) памяти
- Упоминает краевые случаи: дубликаты, нет ответа
Если ответил, копните глубже
Middle: 12 вопросов
Практика: почему так, какие компромиссы, что ломается.
Как работает встроенный DI-контейнер ASP.NET Core и какие ошибки с ним бывают?
Что хотим услышать
- Регистрация в IServiceCollection, разрешение через IServiceProvider, внедрение через конструктор
- Три времени жизни и правило не зависеть от более короткоживущих
- Captive dependency и ValidateScopes
- Scope вручную через IServiceScopeFactory в синглтонах и фоновых службах
- Освобождение IDisposable контейнером
- Service locator как антипаттерн
Если ответил, копните глубже
Спроектируй схему базы для заказов интернет-магазина: клиенты, товары, заказы, оплаты. Какие таблицы, ключи и ограничения?
Что хотим услышать
- Таблицы customers, products, orders, order_items, payments и их связи
- order_items как связь заказа и товара с qty и ценой на момент покупки
- Ограничения: FK, NOT NULL, CHECK (qty > 0), UNIQUE там, где нужно
- Типы: numeric или копейки для денег, timestamptz для времени, статус через CHECK или enum
- Индексы на FK и под частые запросы (заказы клиента по дате)
- Платежи отдельной таблицей с идемпотентным внешним id от платёжки
Если ответил, копните глубже
Чем отличаются сессии на куках и JWT? Что бы ты выбрал для обычного веб-приложения?
Что хотим услышать
- Сессия: id в куке, состояние на сервере (Redis/БД), отзыв мгновенный
- JWT: состояние в самом токене, проверка подписью без базы, сложно отозвать
- Для JWT нужна пара короткий access + отзываемый refresh
- Хранение: HttpOnly кука защищает от XSS, но нужна защита от CSRF (SameSite)
- Выбор по контексту: одно веб-приложение проще на сессиях, много сервисов и мобильные клиенты удобнее на токенах
Если ответил, копните глубже
Расскажите, по какому плану вы будете вести секцию system design на 60 минут.
Что хотим услышать
- Начинаю с уточнения функциональных требований и явно фиксирую, что не делаем
- Уточняю нефункциональные требования: масштаб, задержка, доступность, консистентность
- Делаю грубую оценку RPS и объёмов и использую её в решениях
- Описываю API и модель данных, выбираю хранилище под паттерн доступа
- Рисую высокоуровневую схему и прохожу по ней путь запроса
- Оставляю время на узкие места, отказы, масштабирование и мониторинг
Если ответил, копните глубже
Объясни решение задачи «минимальное число монет для суммы» и его сложность. Почему тут не подходит жадность?
Что хотим услышать
- Показывает контрпример для жадности
- Формулирует состояние: dp[s] минимум монет для суммы s
- Переход: dp[s] = min(dp[s - c] + 1) по монетам
- База dp[0] = 0, остальные Infinity, недостижимо → -1
- O(S · k) времени, O(S) памяти
Если ответил, копните глубже
Как бы ты построил CI/CD для веб-приложения с нуля?
Что хотим услышать
- На каждый PR: npm ci, линтер, типы, unit-тесты, сборка
- Быстрые проверки первыми, независимые джобы параллельно, кеш зависимостей
- Защита ветки main: мерж только с зелёным CI и ревью
- Сборка один раз, артефакт (образ) с версией продвигается по стендам
- Деплой на стенд автоматически, на прод с подтверждением или автоматически при хорошем покрытии
- План отката и миграции, совместимые с предыдущей версией
Если ответил, копните глубже
Почему вы ищете новую работу?
Что хотим услышать
- Причина через цель, а не через бегство
- Ни слова негатива о текущем работодателе
- Показано, что на текущем месте был рост и результаты
- Связка с этой вакансией
- Коротко: до минуты
Если ответил, копните глубже
Расскажите, что происходит с HTTP-запросом от Kestrel до ответа контроллера.
Что хотим услышать
- Kestrel принимает соединение и создаёт HttpContext
- Создаётся DI scope для запроса
- Конвейер middleware: исключения, HTTPS, статика, маршрутизация, аутентификация, авторизация
- Конечная точка: привязка модели, валидация, фильтры (для MVC)
- Выполнение действия и сериализация результата
- Ответ проходит middleware в обратном порядке, scope освобождается
Если ответил, копните глубже
Индекс есть, а в плане Seq Scan. Назови возможные причины и как каждую проверить.
Что хотим услышать
- Низкая селективность: нужна большая доля строк, seq scan дешевле
- Функция или приведение типа над колонкой: нужен индекс по выражению или переписать условие
- Условие не по ведущей колонке составного индекса
- Устаревшая статистика: оценки rows не совпадают с фактом, помогает ANALYZE
- Маленькая таблица, LIKE с % в начале, OR по разным колонкам
- Проверка: EXPLAIN (ANALYZE, BUFFERS), сравнить оценки с фактом
Если ответил, копните глубже
Чем отличаются HTTP/1.1, HTTP/2 и HTTP/3? Какие проблемы решала каждая версия?
Что хотим услышать
- Семантика (методы, статусы, заголовки) одна, меняется транспорт
- HTTP/1.1: запросы по очереди в соединении, браузер держит около 6 соединений
- HTTP/2: бинарные фреймы, мультиплексирование, HPACK, одно соединение
- HTTP/2 упирается в head-of-line blocking TCP при потерях
- HTTP/3: QUIC поверх UDP, независимые потоки, TLS встроен, быстрее рукопожатие, миграция соединения
Если ответил, копните глубже
Какие стратегии кеширования вы знаете и как выбрать подходящую?
Что хотим услышать
- Cache-aside: приложение само читает и заполняет кеш, самый частый вариант
- Read-through: кеш-слой сам загружает данные при промахе
- Write-through: синхронная запись в кеш и БД, свежий кеш, медленнее запись
- Write-behind: асинхронная запись в БД, быстро, риск потери
- Выбор зависит от соотношения чтения и записи и цены устаревших данных
Если ответил, копните глубже
Объясни, как работает BFS, почему он находит кратчайший путь в графе без весов и какая у него сложность.
Что хотим услышать
- Очередь и пометка посещённых при добавлении
- Вершины обрабатываются в порядке неубывания расстояния
- Первое посещение вершины даёт кратчайшее расстояние
- O(V + E) времени и O(V) памяти
- Знает, что при весах нужен Дейкстра
Если ответил, копните глубже
Senior: 12 вопросов
Глубина и опыт: границы подходов, решения в продакшене.
Расскажите, как работает async/await в C под капотом.
Что хотим услышать
- Машина состояний: MoveNext, поля для локальных переменных, номер состояния
- Паттерн awaiter: GetAwaiter, IsCompleted, OnCompleted, GetResult
- Если задача уже завершена, продолжение идёт синхронно без переключений
- Продолжение планируется через SynchronizationContext или TaskScheduler, иначе пул потоков
- Для I/O потоки не заняты: завершение приходит через порты завершения (IOCP, epoll)
- Исключения сохраняются в Task и пробрасываются в await
Если ответил, копните глубже
Расскажите, как в PostgreSQL устроен MVCC, откуда берутся мёртвые строки и зачем нужен VACUUM.
Что хотим услышать
UPDATEсоздаёт новую версию строки, старая остаётся сxmax;DELETEтолько помечает версию- Каждая транзакция видит версии по своему снимку, поэтому чтение не блокирует запись и наоборот
- VACUUM удаляет версии, которые уже никому не видны, обновляет visibility map и замораживает старые строки
- Долгие транзакции и забытые слоты репликации мешают очистке и раздувают таблицы
- Autovacuum настраивают по таблицам:
autovacuum_vacuum_scale_factor, лимиты стоимости; мониторинг черезpg_stat_user_tables - HOT-обновления и
fillfactorуменьшают нагрузку на индексы
Если ответил, копните глубже
VACUUM FULL отличается от обычного?Как бы ты сделал надёжный клиент WebSocket для чата, который переживает обрывы сети?
Что хотим услышать
- Реконнект с экспоненциальной задержкой и джиттером, сброс задержки после успеха
- Heartbeat: ping/pong с таймаутом, при пропуске закрыть и переподключиться
- Нумерация сообщений: после реконнекта клиент сообщает последний id, сервер досылает пропущенное
- Очередь исходящих сообщений с клиентскими id, идемпотентность на сервере против дублей
- Реакция на
online/offlineи видимость вкладки, повторная авторизация при истёкшем токене
Если ответил, копните глубже
Спроектируйте мессенджер с личными и групповыми чатами, доставкой в реальном времени и историей.
Что хотим услышать
- Требования: личные и групповые чаты до 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
Если ответил, копните глубже
Ты приходишь в проект без тестов. Как выстроишь стратегию тестирования?
Что хотим услышать
- начать с самого рискованного и ценного для бизнеса: оплата, вход, ключевые сценарии
- несколько e2e smoke-тестов на критичные пути
- unit-тесты на чистую логику: расчёты, форматирование, утилиты
- интеграционные тесты компонентов и API на новых и изменяемых местах
- тесты в CI как обязательная проверка PR
- регрессионный тест на каждый найденный баг, борьба с флаки
Если ответил, копните глубже
Вы претендуете на senior. Расскажите о себе.
Что хотим услышать
- Назван масштаб: команда, нагрузка, пользователи или деньги
- Есть решения, которые повлияли дальше своего кода
- Есть метрики результата
- Упомянуто влияние на команду: менторинг, процессы, ревью
- Уложился в 2 минуты, не утонул в деталях
Если ответил, копните глубже
Расскажите, как устроен Dictionary<TKey, TValue в .NET.
Что хотим услышать
- Два массива: buckets (индексы) и entries (хеш, next, ключ, значение)
- Индекс бакета: хеш по модулю размера, размер простое число
- Коллизии решаются цепочками через поле next внутри массива entries
- Удалённые слоты собираются в free list и переиспользуются
- При заполнении массивы растут примерно вдвое до простого числа с перехешированием
- Не потокобезопасен: одновременная запись может сломать структуру
Если ответил, копните глубже
На проде в логах появились ошибки deadlock detected. Как вы будете разбираться и что сделаете, чтобы они ушли?
Что хотим услышать
- Deadlock это цикл ожидания: транзакции держат блокировки и ждут друг друга
- PostgreSQL проверяет цикл после
deadlock_timeout(1 с по умолчанию) и откатывает одну из транзакций с ошибкой 40P01 - В логе есть оба запроса и тип блокировок; включаю
log_lock_waitsдля долгих ожиданий - Главное лечение: единый порядок взятия блокировок, короткие транзакции, блокировка заранее через
SELECT ... FOR UPDATEв порядке ключа - Приложение должно уметь повторять транзакцию при 40P01 и 40001
Если ответил, копните глубже
FOR UPDATE отличается от FOR NO KEY UPDATE?lock_timeout?Первая загрузка сайта занимает 4 секунды у пользователей из регионов. Как бы ты искал, где теряется время, и что делал?
Что хотим услышать
- Измерить: Navigation Timing и RUM по этапам (DNS, connect, TLS, TTFB, загрузка, рендер), Lighthouse и WebPageTest
- Сеть: CDN ближе к пользователю, HTTP/2 или HTTP/3, preconnect к сторонним доменам
- Сервер: медленный TTFB → кеширование, отдача статики с CDN, 103 Early Hints
- Ресурсы: Brotli, меньше JS, code splitting, оптимизированные картинки с размерами
- Рендер: критический CSS, defer для скриптов, без блокирующих сторонних скриптов
Если ответил, копните глубже
Спроектируйте ленту новостей соцсети: пользователь подписан на других и видит их посты, новые сверху.
Что хотим услышать
- Требования: публикация поста, подписка, лента с пагинацией; порядок по времени (ранжирование вне рамок или упрощённо); лента грузится < 200 мс, допустима задержка появления поста в секунды
- Оценка нагрузки: 100 млн DAU, 10 открытий ленты в день ≈ 10 000 RPS чтения, в пике ~30 000; 10 млн постов в сутки ≈ 100 RPS записи; при 200 подписчиках в среднем ≈ 20 000 вставок в ленты в секунду
- API: POST /posts; GET /feed?cursor=...&limit=20; POST /users/{id}/follow
- Данные: посты в шардированной БД по author_id, граф подписок отдельно, готовые ленты как списки id постов в Redis (последние ~500), медиа в S3
- Компоненты: сервис постов, очередь событий публикации, fan-out воркеры, сервис ленты, кеш постов, CDN для медиа
- Узкие места: аккаунты-звёзды (гибрид on write + on read), неактивные пользователи (не раскладывать, собирать при входе), пагинация курсором, память Redis, удаление и редактирование постов
Если ответил, копните глубже
Чем, по-вашему, резюме senior должно отличаться от резюме middle?
Что хотим услышать
- Акцент на масштаб и влияние, а не на список фич
- Метрики бизнес-эффекта
- Лидерство: менторинг, процессы, архитектурные решения
- Компактность: старый опыт свёрнут
- Понимание, что резюме под роль, а не под всё сразу
Если ответил, копните глубже
Когда вы используете события C, а когда выбираете другие механизмы (интерфейсы-наблюдатели, IObservable, MediatR, очереди)?
Что хотим услышать
- События подходят для синхронных уведомлений внутри процесса и UI
- Минусы событий: утечки при забытой отписке, синхронность, исключение одного подписчика прерывает остальных
- Async-обработчики в событиях плохо работают (async void)
- IObservable и Rx для потоков событий с операторами и отпиской через IDisposable
- Внутрипроцессный медиатор для доменных событий и слабой связности
- Межсервисное взаимодействие через брокер (RabbitMQ, Kafka) с гарантиями доставки
Если ответил, копните глубже
Задача для live coding
Трассировка конвейера middleware
Смоделируйте конвейер middleware. Каждый элемент одного из трёх видов:
name pass: пишетname>, вызывает следующий, после возврата пишет<name;name block PREFIX CODE: если путь начинается сPREFIX, пишетname!и завершает запрос с кодомCODE, не вызывая следующий; иначе ведёт себя какpass;name endpoint PATH: если путь в точности равенPATH, пишетname#и отвечает 200; иначе молча передаёт дальше (как маршрутизация, не нашедшая совпадения).
Если запрос дошёл до конца конвейера, ответ 404.
Ввод: n (n ≤ 30) элементов, затем q (q ≤ 3000) путей.
Вывод: для каждого пути журнал через пробел, затем = CODE.
Пример
4
log pass
auth block /admin 401
health endpoint /health
api endpoint /api
3
/health
/admin/users
/missing
log> auth> health# <auth <log = 200
log> auth! <log = 401
log> auth> <auth <log = 404
Эталон, тесты и таймер открываются интервьюеру в живом моке
Рубрика оценки: 1-4 балла
Те же критерии процесса, что в живом моке Техпрогона. Ставьте балл по каждому пункту, сумма показывает картину лучше, чем «понравился или нет».
Уточнил условие
Ограничения, формат входа, что вернуть при пустом вводе
Привёл примеры до кода
Обычный случай и хотя бы один краевой
Проговорил алгоритм словами
До того как писать код
Назвал сложность
По времени и по памяти
Написал рабочий код
Тесты прошли
Сам проверил краевые случаи
Прогнал в голове пустой ввод, один элемент, дубликаты
Нашёл и исправил ошибку сам
Или ошибок не было с первого раза
Мок-собес без регистрации для интервьюера
Хотите проверить кандидата на задаче с эталоном и рубрикой? Так работает живой мок в Техпрогоне.
Кандидат готовится в Техпрогоне и на задаче открывает ступень «Живой мок».
Он присылает вам ссылку. Вы открываете её без регистрации.
У вас условие, эталон, рубрика, уточняющие вопросы и таймер.
Вы ставите оценки и вердикт, кандидат сразу видит результат.
Для интервьюера бесплатно. Ссылку создаёт кандидат на тарифе Рывок
Наборы для других ролей
Готовитесь к собесу сами? Подготовка к собеседованию: Backend на C#/.NET