IT/программирование

Как проходят собесы на Senior Backend: Go, Python и универсальный роадмап

Команда ZUB-AI разобрала три материала по техническим собеседованиям на Senior Backend. Внутри: реальные вопросы, типичные ошибки, роадмап по 7 блокам для Go и Python разработчиков.

Как проходят собесы на Senior Backend: Go, Python и универсальный роадмап

Как проходят собесы на Senior Backend: Go, Python и универсальный роадмап

Мы посмотрели несколько часов контента по техническим собеседованиям на Senior и Middle/Senior Backend позиции — живые мок-интервью и структурированные роадмапы. Охват широкий: Go с его внутренностями рантайма, Python с GIL и PostgreSQL, плюс универсальная карта знаний для любого бэкенд-стека.

Материал будет полезен тем, кто готовится к собесу в ближайшие недели, а не «когда-нибудь потом». Здесь нет воды про «будь уверен в себе» — только конкретные темы, типичные ошибки кандидатов и то, что реально спрашивают на разных уровнях.

Главный вывод, который прослеживается во всех источниках: платят на Senior не за знание синтаксиса, а за понимание того, почему код работает именно так и где он сломается в продакшене.


Что проверяют на Senior Go: внутренности, а не API

Слайсы и map: вопрос не «что», а «почему»

На джуниорском собесе достаточно знать, что append увеличивает слайс. На Senior от тебя ожидают другого уровня разговора.

Slice header — это структура из трёх полей: указатель на массив, длина (len) и ёмкость (cap). При передаче слайса в функцию копируется именно этот заголовок. Отсюда важное следствие: изменения длины внутри функции снаружи не видны, но запись в общий массив — видна всем, кто держит ссылку на тот же backing array. Это классический источник неожиданных мутаций.

При переполнении capacity рантайм выделяет новый массив и копирует данные. Правила роста capacity — не контракт обратной совместимости. Они менялись между версиями Go и могут измениться снова. Рассчитывать на конкретный коэффициент роста в продакшн-коде — плохая идея.

По map — здесь свежий материал. Начиная с Go 1.24 появились SwissTable map. Старая реализация хранила данные в бакетах по 8 значений, коллизии решались через overflow buckets по указателю. Новая использует открытую адресацию.

Как работает поиск в SwissTable? Хеш ключа делится на две части: - H1 — старшие биты, определяют группу из 8 слотов - H2 — младшие 7 бит, записываются в control byte каждого слота как метаданные

Сначала идёт поиск по метаданным всей группы за одну SIMD-инструкцию. Полное сравнение ключа запускается только при совпадении control byte. Это быстрее, чем перебирать бакеты подряд.

SIMD (Single Instruction Multiple Data) — не многоядерный параллелизм. (17:00) ▶ 17:00

SIMD (Single Instruction Multiple Data) — не многоядерный параллелизм. Один такт, один регистр, одна инструкция обрабатывает пачку данных. На x86 это SSE-регистры на 128 бит (16 байт или 4 значения float32). Go-компилятор, в отличие от LLVM, не векторизует циклы автоматически.

Коллизии в хеш-функциях неизбежны — это следствие принципа Дирихле: бесконечное множество входов отображается в конечное множество хеш-значений.

time.Now() и память: детали, которые отличают Senior

time.Now() содержит два компонента: настенные часы (wall clock) и монотонные часы (monotonic clock). Настенные могут идти назад — например, при синхронизации NTP. Монотонные — только вперёд. При вычитании двух значений time.Time Go использует монотонные часы, поэтому разница всегда корректна. Казалось бы, деталь — но именно такие детали вскрываются на реальных собесах.

По управлению памятью: после сборки мусора память не возвращается в ОС немедленно. Она остаётся в аллокаторе рантайма для переиспользования. Обратно в ОС её отдаёт фоновый процесс. Это важно понимать, когда смотришь на RSS процесса и удивляешься, почему он не падает после нагрузочного теста.

Нетипизированные константы в Go не существуют в рантайме вообще. Компилятор вычисляет их на этапе компиляции и подставляет в места использования. Адреса в памяти у них нет.

Escape-анализ определяет, куда попадёт переменная — на стек или в кучу. Стек дешевле: выделение и освобождение за O(1). Куча требует участия GC. Если переменная «убегает» наружу (например, её адрес уходит в функцию или интерфейс), компилятор кладёт её в кучу.

Горутины: классические ловушки на живом коде

На собесах по Go почти всегда есть задача с горутинами, где нужно найти баг. Вот типичный паттерн:

// Проблемный код
func sendAll(senders []Sender) error {
    ch := make(chan error)
    for _, s := range senders {
        go func(s Sender) {
            ch <- s.Send()
        }(s)
    }
    for range senders {
        if err := <-ch; err != nil {
            return err // выходим при первой ошибке
        }
    }
    return nil
}

Проблема: при выходе по первой ошибке оставшиеся горутины пытаются записать в канал, но читателя уже нет. Они висят на ch <- навсегда — утечка горутин. Память не освобождается, процесс тихо распухает.

Корректное решение — errgroup или явная обработка через context с отменой. (23:25) ▶ 23:25

Корректное решение — errgroup или явная обработка через context с отменой. Важный момент по recover(): он ловит панику только в той же горутине, где вызывается. Паника из одной горутины в другую не всплывает. Паника в горутине без recover кладёт весь процесс целиком.


Python Middle/Senior: GIL, PostgreSQL и Redis в деталях

GIL и параллелизм: честный разговор

GIL (Global Interpreter Lock) — механизм, который не позволяет более чем одному потоку CPython исполняться одновременно. Это означает: для CPU-bound задач (вычисления, обработка данных) многопоточность в Python не даёт реального параллелизма. Потоки переключаются, но не выполняются одновременно.

Для IO-bound задач (сеть, файлы, база) GIL почти не мешает: пока один поток ждёт ответа от сети, другой может работать.

Экспериментальное отключение GIL появилось в Python 3.14, но до продакшн-использования ещё далеко. Пока реальный параллелизм в Python достигается через multiprocessing (отдельные процессы) или asyncio (кооперативная многозадачность для IO).

Go в этом отношении принципиально другой: горутины не имеют аналога GIL, их можно запускать тысячами, планировщик Go распределяет их по нескольким OS-потокам.

Контекст-свитчинг между потоками — операция не бесплатная, прежде всего из-за сохранения и восстановления стека. За переключение отвечает планировщик ОС.

Семафоры ограничивают количество потоков, одновременно работающих с ресурсом — например, не более трёх одновременно. Локи работают жёстче: пока один поток пишет, другие не могут ни читать, ни писать.

PostgreSQL: индексы и транзакции без лишних слов

Самые частые темы на собесах по базам данных — индексы и уровни изоляции.

B-tree индекс — самый распространённый. Сложность поиска O(log n), вставка тоже логарифмическая (нужно найти узел и, возможно, перебалансировать дерево). Подходит для большинства случаев.

GIN-индекс — для сложных структур: JSONB, массивы, полнотекстовый поиск. При полнотекстовом поиске текст преобразуется в tsvector, поиск идёт по вектору.

Ключевое понятие — селективность: насколько уникальны значения поля. Поле email — высокая селективность, индекс оправдан. Поле gender с тремя возможными значениями — низкая селективность, B-tree индекс не поможет. Можно построить — но планировщик PostgreSQL скорее всего всё равно выберет Seq Scan.

EXPLAIN ANALYZE — запускает запрос реально, показывает фактическое время и количество строк. На продакшн-базе использовать с осторожностью: тяжёлый запрос нагрузит сервер. Смотреть нужно на тип сканирования (Seq Scan без индекса, Index Scan с индексом), количество строк (rows) и стоимость (cost).

SELECT FOR UPDATE блокирует строки до конца транзакции. Применяется в паттерне «прочитай-вычисли-запиши», чтобы никто не изменил строку в промежутке. Полезно, но увеличивает время ответа — использовать точечно.

MVCC (Multi-Version Concurrency Control) — механизм версионирования: при записи создаётся новая версия, читатели видят предыдущую. Это позволяет чтению не блокировать запись.

По уровням изоляции: READ COMMITTED — стандарт для большинства задач. SERIALIZABLE — выстраивает транзакции последовательно, исключает все аномалии (фантомное чтение, неповторяемое чтение), но жертвует производительностью. Используется в финансовых системах, где аномалии недопустимы.

Redis: что реально спрашивают

Redis по умолчанию однопоточный. Это значит, что команды выполняются последовательно. KEYS * перебирает все ключи базы — на нагруженном инстансе с миллионами ключей это блокирует все операции на секунды. На продакшне запрещена. Вместо неё — SCAN с итерацией и курсором.

Redis Cluster использует мастер-ноды и реплики. Ключи распределяются по нодам через алгоритм консистентного хэширования (hash ring). Преимущество: при добавлении или удалении ноды перераспределяется минимальное количество ключей, а не все сразу — в отличие от классического модульного хэширования.

Метрики качества кэша: hit rate (доля запросов, нашедших данные в кэше) и miss rate (доля промахов). Если пользователи запрашивают уникальные данные — кэш будет постоянно промахиваться и лишь добавит latency. Кэш имеет смысл только при достаточно высоком hit rate.

Сериализация объектов для Redis: JSON — самый простой, бинарные форматы (MessagePack и аналоги) — занимают меньше места и быстрее при передаче, но требуют CPU на упаковку/распаковку. pickle работает только внутри Python.


Универсальный роадмап: 7 блоков для любого бэкенд-стека

Разберём по блокам то, что реально проверяют на собеседованиях — от скрининга до Senior-раунда.

Блок 1. Computer Science и многопоточность

Самый заезженный вопрос на скрининге: процесс vs поток. Процесс — это запущенный экземпляр приложения со своей областью памяти. Потоки существуют внутри процесса и делят его общую память. Между процессами прямой доступ к памяти друг друга невозможен — только через IPC (inter-process communication).

Виды IPC, которые нужно перечислить: файл, сигнал, сокет, конвейер, именованный… (04:56) ▶ 04:56

Виды IPC, которые нужно перечислить: файл, сигнал, сокет, конвейер, именованный канал, семафор, общая память, очереди сообщений. Файлы, сигналы и сокеты работают во всех ОС. Остальное — преимущественно POSIX (Linux).

Лёгкие потоки в разных языках называются по-разному: - Go — горутины - Kotlin — корутины - PHP — файберы - Java — виртуальные треды (Green Threads)

Две модели межпотокового взаимодействия: классическая через общую память… (01:14) ▶ 01:14

Две модели межпотокового взаимодействия: классическая через общую память (большинство языков) и CSP (теория последовательных процессов) — каналы, селекты, горутины. Последнее — специфика Go.

Конкурентность vs параллелизм — частый вопрос. Конкурентность: несколько задач в процессе выполнения одновременно (могут не выполняться буквально в один момент). Параллелизм: задачи выполняются буквально одновременно на разных ядрах или процессорах.

Блок 2. Сети и протоколы

HTTP 1.1 и HTTP/2 — обязательно. HTTP/2 лежит в основе gRPC.

gRPC — не самостоятельная технология, а частный случай RPC (Remote Procedure Call). Работает поверх HTTP/2, использует Protocol Buffers для сериализации. Знание, что существует ещё и SOAP как второй вид RPC (устаревший, но живой в корпоративных системах) — это уже уровень Senior на собесе.

JWT — просто формат токена. (03:11) ▶ 03:11

JWT — просто формат токена. Не аутентификация, не авторизация. Частая путаница на собесах — смешивать эти понятия.

Блок 3. Безопасность

Аутентификация vs авторизация — самый частый вопрос блока на скрининге.

Виды аутентификации: - Basic Auth — устаревший, допустим только для небольших внутренних систем - OAuth 2.0 + OpenID Connect — стандарт для большинства современных систем, реализует SSO - Keycloak — готовое опенсорс-решение для OAuth 2.0, разворачивается как сервис, настраивается через UI

Виды авторизации: - RBAC — доступ по ролям - ABAC — доступ по атрибутам объекта и субъекта - Policy-based — доступ по правилам (используется в OPA и аналогах)

Блок 4. Микросервисы

Тема обязательна для мидла. На скрининге спросят отличие от монолита. На Senior — паттерны.

Паттерны данных: - CQRS — разделение операций чтения и записи на разные модели - Saga — управление транзакционностью между сервисами через компенсирующие действия - Database per Service — каждый сервис владеет своей базой - Eventual Consistency — финальная согласованность. Перед собесом стоит знать теорему CAP

Transactional Outbox — тема уровня Senior, встречается редко, но знание выделяет.

Паттерны отказоустойчивости: - Retry — повторы при сбоях с экспоненциальным backoff - Circuit Breaker — при систематических сбоях функционал отключается, не нагружая систему - Bulkhead — изоляция отказа: если тонет один компонент, остальные работают (аналогия с переборками корабля)

Паттерны развёртывания: - Canary Release — поэтапный выкат: 1% → 10% → 20% → все пользователи - Feature Toggle — включение/отключение функционала без деплоя

Блок 5. Продакшн-инфраструктура

Мониторинг: Prometheus собирает метрики, Grafana их визуализирует. Grafana сама по себе метрики не собирает — это частая путаница на собесах.

Трассировка: OpenTelemetry — наиболее популярный стандарт. Jaeger и Zipkin — конкретные реализации.

По Kubernetes достаточно уверенно работать как пользователь: kubectl, подключиться к поду, посмотреть логи, зайти в консоль пода. Глубокое знание Kubernetes-контроллеров — это отдельная специализация (и отдельная зарплата).

Очереди сообщений: Kafka, RabbitMQ и NATS покрывают подавляющее большинство собеседований. Ansible и Jenkins — DevOps-тематика, бэкенд-разработчику знать необязательно.

Блок 6. Базы данных

SQL-темы уровня Senior: - Оконные функции (OVER, PARTITION BY, ROW_NUMBER) —… (13:05) ▶ 13:05

SQL-темы уровня Senior: - Оконные функции (OVER, PARTITION BY, ROW_NUMBER) — встречаются на практических задачах - Транзакции и уровни изоляции — обязательно - ACID — атомарность, согласованность, изолированность, устойчивость - EXPLAIN и EXPLAIN ANALYZE — умение читать план запроса

Индексы: B-tree — база. GIN — для JSONB и полнотекстового поиска. Bitmap и GiST — спрашиваются редко.

NoSQL: Redis и MongoDB — активно проверяются. Знание Redis в деталях (однопоточность, KEYS *, консистентное хэширование) выделяет кандидата.

Паттерны работы с данными: Repository — распространённый, Active Record — многие считают антипаттерном из-за смешивания бизнес-логики с доступом к данным.

Блок 7. Паттерны проектирования

Из классики «Банды четырёх» на собесах чаще всего встречаются:

Порождающие: Singleton, Фабричный метод, Абстрактная фабрика, Lazy Initialization.

Структурные: Декоратор, Фасад, Адаптер, Proxy.

Поведенческие: Стратегия, Observer (Наблюдатель), Команда.

Паттерны параллелизма: Thread Pool — очень популярен, особенно в Java. Double-Checked Locking — спрашивается реже, но знание нужно.

Архитектурные подходы: чистая архитектура со слоями domain/application/service/controller и гексагональная архитектура с портами и адаптерами — разные способы достичь одного результата: тестируемой доменной логики, не зависящей от конкретных технологий.


Практический раздел: что делать прямо сейчас

Если собес в ближайшие 2-4 недели — разберём по приоритетам.

Go-разработчикам: 1. Проговори вслух внутреннее устройство slice header и механизм роста capacity. Не читай — объясни, как будто перед интервьюером. 2. Разберись с новыми SwissTable map (Go 1.24): H1/H2 разбивка хеша, control bytes, SIMD-поиск. 3. Напиши тест с намеренной утечкой горутин через канал без получателя — убедись, что можешь воспроизвести и исправить. 4. Проверь, что понимаешь разницу wall clock / monotonic clock в time.Now(). 5. Пройдись по escape-анализу: скомпилируй что-нибудь с флагом -gcflags="-m" и посмотри, что куда уходит.

Python-разработчикам: 1. Объясни GIL и его влияние на CPU-bound vs IO-bound задачи — без бумаги, вслух. 2. Разберись с SELECT FOR UPDATE и MVCC в PostgreSQL: когда применять, какие накладные расходы. 3. Попрактикуй EXPLAIN ANALYZE: возьми реальный запрос, посмотри план, найди Seq Scan там, где должен быть Index Scan. 4. Проверь: можешь ли ты объяснить, почему KEYS * в Redis опасен и что использовать вместо него? 5. Реализуй Dependency Injection в FastAPI через Depends — если ещё не делал в последнее время, освежи.

Всем, кто готовится к собесу по архитектуре: 1. Выучи Saga и CQRS с примерами — не определение, а когда применять. 2. Объясни разницу Circuit Breaker и Retry — это разные паттерны с разными сценариями применения. 3. Разберись с Canary Release на конкретном примере деплоя. 4. Проверь понимание Prometheus + Grafana: что именно делает каждый инструмент.


Что мы заметили: где подходы сходятся, где расходятся

Сходится во всех источниках:

Граница между Middle и Senior проходит не по знанию новых фреймворков, а по глубине понимания платформы. Знать, что append расширяет слайс — это Middle. Объяснить правила роста capacity, поведение при передаче в функцию и почему изменения длины не видны снаружи — это Senior.

Умение находить и объяснять продакшн-баги ценится выше, чем знание теоретических определений. Утечка горутин, race condition при параллельном доступе к map, неправильная семантика передачи slice — всё это реальные баги, которые стоят компаниям денег. Кандидат, который видит такой код и сразу объясняет механизм на уровне рантайма, — это тот, за кем охотятся.

Где подходы немного расходятся:

Более детальный разбор уделяет большое внимание внутреннему устройству конкретного языка — вплоть до разбивки хеша на H1/H2 и работы SIMD-инструкций. Это оправдано для позиций, где от тебя ожидают оптимизации производительности и знания платформы на уровне рантайма.

Роадмап-подход, напротив, ориентирован на ширину охвата: семь блоков, приоритеты по уровням, универсальность для любого стека. Здесь важнее знать, что такое CQRS и Saga, чем объяснять детали SwissTable. Это не противоречие — это разные фокусы для разных позиций.

Более консервативный взгляд на собеседование: интервьюер оценивает не только точность ответов, но и ход мысли. Рассуждение вслух, движение к правильному ответу через логику, умение сказать «не знаю точно, но логика подсказывает вот так» — это ценится. Противоположный подход: знания должны быть точными, потому что в продакшне нет интервьюера, который направит тебя в нужную сторону.

На наш взгляд, правда посередине: ход мысли важен, но только если он движется в правильном направлении. Полная неопределённость по базовым темам — например, не знать, что GIL влияет только на CPU-bound задачи — не вытягивается никаким «рассуждением вслух».


Итог

Senior Backend — это не тот, кто знает больше фреймворков. Это тот, кто может объяснить, что происходит под капотом, найти баг до того, как он попал в продакшн, и объяснить коллеге, почему именно так, а не иначе.

Семь блоков роадмапа дают карту. Разбор живых собесов по Go и Python показывает, что именно на этой карте проверяется острее всего. Возьми оба — и закрой пробелы не «для галочки», а так, чтобы объяснить вслух без подсказок. Именно это и есть разница между кандидатом, которому делают оффер, и тем, кому говорят «мы вам перезвоним».

Использованные видео

Хотите такой же разбор для своего видео?

ZUB-AI проанализирует видео с YouTube, RuTube или VK и пришлёт структурированный отчёт. Первый анализ — бесплатно.

Попробовать ZUB-AI →