RecallDeck
Направление

Подготовка к собеседованию — Backend-инженер

Колода из 625+ карточек с вопросами для собеседования по направлению «Backend-инженер» — по темам и уровню сложности, с возвратом ровно перед тем, как вы забудете. Посмотрите несколько карточек ниже, затем выберите доступ, чтобы учить всё направление по расписанию в стиле Anki (SM-2).

625 карточек22 темы
Посмотреть цены и начать

7 дней бесплатно на месяц или год · все функции включены.

Что внутри

Все темы направления, сгруппированные так, как вы будете их учить.

Распределённые системы

12 карточек
Распределённые системы

Kafka и очереди

12 карточек
Kafka и очереди

Кэширование вглубь

10 карточек
Кэширование

Производительность и конкурентность

11 карточек
Производительность

Надёжность и инциденты

10 карточек
Надёжность и инциденты

Python

122 карточки
Ядро языкаМодель данных и внутренностиКонкурентность и asyncStdlib, типизация и тесты

Базы данных

104 карточки
Основы SQLВнутренности Postgres и оптимизацияNoSQL и Redis

Бэкенд

169 карточек
HTTP и REST APIАрхитектура и масштабированиеАутентификация и безопасностьDjango и FastAPIORM и SQLAlchemy

Основы CS

76 карточек
Структуры данных и алгоритмыСети

DevOps и инфраструктура

35 карточек
Docker, CI/CD и Linux

Системный дизайн

29 карточек
Системный дизайн

Поведенческое интервью

35 карточек
Поведенческое интервью

Примеры вопросов

Несколько карточек из колоды — откройте ответ, затем выберите доступ, чтобы учить весь набор по расписанию.

Event sourcing и CQRS: когда они оправданы и какова цена?

Короткий ответ: Event sourcing: состояние не хранится, а выводится — state = fold(events); первичен журнал событий. CQRS: модель записи и модели чтения разделены. Дают полный аудит, temporal queries («как выглядел заказ вчера»), пересборку проекций с нуля. Цена высокая: версионирование событий, снапшоты, eventually consistent read-модели, дорогой тулинг. Это не архитектура по умолчанию.

Подробно:

events:  OrderCreated → ItemAdded → ItemAdded → OrderPaid
state  = fold(events)   — всегда выводимо заново
проекции: «заказы за день», «топ товаров» — свои read-модели,
          можно пересобрать из журнала с нуля
  1. Когда оправдано — движение денег, леджеры, домены с жёстким аудитом и комплаенсом; «почему баланс стал таким» — вопрос бизнеса, а не логов.
  2. Цена №1: версионирование — событие живёт вечно; изменилась схема — читать все старые версии (upcasters) или мигрировать журнал целиком.
  3. Цена №2: чтение — read-модели асинхронны: после команды проекция отстаёт, и UI, тесты, саппорт обязаны это переживать.
  4. CQRS без ES — легитимен и сильно дешевле: обычная БД записи + денормализованные проекции для чтения.

⚠️ Частая ошибка: предлагать event sourcing для CRUD-приложения «на вырост». Если аудит — не бизнес-требование, вы платите всю цену ES и не получаете ничего, что не дала бы таблица плюс журнал изменений.

Как консюмер-группа распределяет партиции и что будет, если консюмеров больше, чем партиций?

Короткий ответ: Внутри группы каждая партиция читается максимум одним консюмером. Консюмеров больше, чем партиций — лишние простаивают: максимальный параллелизм группы равен числу партиций. Разные группы читают один топик независимо, у каждой свои оффсеты.

Подробно:

topic orders: p0  p1  p2  p3
group A (3 консюмера):
  c1 ◄─ p0, p1    c2 ◄─ p2    c3 ◄─ p3
group A (6 консюмеров):
  c1◄p0  c2◄p1  c3◄p2  c4◄p3   c5, c6 — idle
group B: читает те же партиции независимо (свои оффсеты)
  1. Внутри группы — партиция достаётся ровно одному консюмеру: так сохраняется порядок обработки внутри партиции.
  2. Между группами — независимое чтение: каждая группа коммитит свои оффсеты в __consumer_offsets, одни и те же данные обслуживают и биллинг, и аналитику.
  3. Планирование — число партиций задаёт потолок масштабирования группы; закладывайте его с запасом при создании топика.

⚠️ Частая ошибка: «добавим консюмеров — станет быстрее». После числа партиций добавленные консюмеры просто простаивают.

Cache-aside, write-through, write-behind: как работает каждая стратегия и когда какую выбрать?

Короткий ответ: Cache-aside — приложение читает кэш, на промахе идёт в БД и кладёт результат; на запись пишет в БД и инвалидирует ключ. Write-through — запись синхронно в кэш и в хранилище. Write-behind — ack от кэша, в хранилище сбрасывается асинхронно. Дефолт — cache-aside; остальные две — под конкретные профили нагрузки.

Подробно:

Cache-aside Write-through Write-behind
Чтение miss → БД → кэш из кэша из кэша
Запись БД + инвалидировать ключ кэш + БД синхронно кэш сразу, БД потом
Консистентность окно stale после записи читатели видят свежее слабая до сброса
Латентность записи как у БД БД + кэш: медленнее только кэш: быстро
Риск потери нет нет есть: упал до сброса
  1. Cache-aside — кэш не стоит на критическом пути записи и его падение переживается (просто больше промахов); цена — свой протокол инвалидации на каждую запись.
  2. Write-through — консистентные чтения из коробки; цена — каждая запись ждёт оба хранилища, и кэшируется в том числе то, что никто не прочитает.
  3. Write-behind — буфер для шквала записей (счётчики, лайки, метрики); без durable-буфера (очередь, AOF) падение узла = потерянные записи.

⚠️ Частая ошибка: выбирать write-behind «для скорости», не ответив, что случится с несброшенными записями при падении процесса.

N+1-запросы: как их заметить и как чинить?

Короткий ответ: N+1 — один запрос за списком и ещё по запросу на каждый элемент. Симптом: латентность растёт линейно с размером коллекции, а в логе — серия одинаковых по форме запросов, отличающихся только id. Лечится жадной загрузкой или батчингом по id.

Подробно:

  1. Заметить — лог SQL (echo, debug toolbar): десятки одинаковых SELECT … WHERE id = ?; в тестах — ассерт на число запросов (django_assert_num_queries, счётчик на событиях SQLAlchemy).
  2. Починить — жадная загрузка: select_related/prefetch_related в Django, joinedload/selectinload в SQLAlchemy; либо вручную собрать id и сделать один WHERE id IN (…).
  3. Закрепить — тем же тестом на число запросов: регрессия не проедет в прод молча.
# было: 1 + N запросов
for order in orders:
    print(order.customer.name)   # запрос на каждой итерации

# стало: JOIN, один запрос
orders = Order.objects.select_related("customer")

⚠️ Частая ошибка: сделать prefetch, а потом в цикле дёрнуть .filter()/.count() по связанной коллекции — ORM снова уходит в БД на каждой итерации, и N+1 возвращается, хотя «префетч же есть».

Таймаут провайдера в середине платежа: деньги списаны или нет? Что делает ваш код?

Короткий ответ: Неизвестно — таймаут значит «ответ не дошёл», а не «операция не выполнилась». Слепой ретрай неидемпотентного списания — прямой путь к двойному списанию. Правильно: запросить статус операции у провайдера / дождаться вебхука, а финальную согласованность даёт сверка (reconciliation) по реестрам провайдера.

Подробно:

t0  наш сервис ──► POST /charge ──► провайдер
t1  провайдер списал деньги ✓
t2  ответ потерялся в сети ✗
t3  у нас таймаут: исход НЕИЗВЕСТЕН (окно t1–t3)
  1. Три неразличимых исхода — запрос не дошёл; провайдер упал посередине; ответ потерялся на обратном пути. Снаружи они одинаковы.
  2. Не ретраить вслепую — платёж у нас остаётся в PENDING; опрашиваем статус-API провайдера или ждём вебхук, и только по факту двигаем state machine.
  3. Идемпотентность у провайдера — передаём провайдеру свой ключ идемпотентности: тогда даже ретрай безопасен by design, дубль он отобьёт сам.
  4. Сверка — регулярная джоба сравнивает наши подвисшие/итоговые статусы с отчётами провайдера и закрывает расхождения: деньги не теряются молча.

⚠️ Частая ошибка: трактовать таймаут как «не списалось» и пометить платёж FAILED — клиент платит второй раз, а первое списание всплывает потом при сверке.

Чем отличаются изменяемые (mutable) и неизменяемые (immutable) типы?

Короткий ответ: Изменяемые объекты можно менять «на месте» без создания нового объекта (list, dict, set, bytearray), неизменяемые — нет (int, float, str, bytes, tuple, frozenset, bool, None). Любое «изменение» неизменяемого создаёт новый объект.

Подробно: Изменяемость — это про то, может ли объект поменять своё содержимое, сохранив тот же id() (адрес в памяти).

# Неизменяемый: операция создаёт НОВЫЙ объект
s = "hello"
print(id(s))
s += " world"      # создаётся новая строка
print(id(s))       # id поменялся — это другой объект

# Изменяемый: меняется тот же объект
lst = [1, 2, 3]
print(id(lst))
lst.append(4)      # меняем на месте
print(id(lst))     # id тот же

Почему это важно:

  • Хешируемость. Только неизменяемые (по содержимому) объекты можно класть в dict-ключи и set. Если бы ключ менялся, его хеш «уехал» бы, и его нельзя было бы найти.
  • Безопасность при шаринге. Неизменяемый объект можно безопасно расшарить между потоками/функциями — никто его не испортит.
  • Аргументы функций. Python передаёт ссылку на тот же объект: функция может изменить содержимое изменяемого аргумента, но локальное переназначение параметра не меняет переменную вызывающего кода.

⚠️ Ловушка: tuple неизменяем, но если в нём лежит list, этот список можно поменять:

t = ([1, 2], 3)
t[0].append(99)   # OK! список внутри мутабелен
print(t)          # ([1, 2, 99], 3)
# t[0] = [...]    # вот ЭТО — TypeError, переприсвоить элемент нельзя

А ещё hash(([1,2], 3)) упадёт — кортеж нехешируем, если внутри есть нехешируемый элемент.

Готовы закрепить навсегда?

Первая сессия — меньше минуты. Ваше будущее «я» на собеседовании скажет спасибо.

Вопросы об этом направлении

Как готовиться к собеседованию на «Backend-инженер»?

Учите концепции, которые придётся объяснять, а не только те, что умеете кодить. Направление «Backend-инженер» в RecallDeck даёт 625+ отобранных вопросов и возвращает каждый по расписанию в стиле Anki (SM-2) ровно перед тем, как вы забудете — чтобы на собеседовании ответы были под рукой.

Какие темы охватывает направление «Backend-инженер»?

Направление «Backend-инженер» разбито на ключевые области, которые реально проверяют на таких собеседованиях, — по темам и уровню сложности (Concept, Junior, Middle, Senior). Полный план и примеры вопросов можно посмотреть выше до входа.

Помогает ли интервальное повторение в подготовке к «Backend-инженер»?

Да. Активно вспоминать ответ и честно себя оценивать — куда прочнее для памяти, чем перечитывать заметки. RecallDeck планирует каждую карту «Backend-инженер» так, чтобы она вернулась перед моментом забывания: ежедневных повторений становится меньше, а знания держатся.

Можно попробовать направление «Backend-инженер» до оплаты?

Да. Месячный и годовой варианты включают семь пробных дней со всем направлением «Backend-инженер», полным планировщиком SM-2, статистикой, гибким темпом и блиц-режимом. Отменить можно онлайн до первого списания.

Другие направления

RecallDeckПодготовка к собеседованию на интервальных повторениях