Курс B · Mobile System Design

Mobile / iOS System Design

Трек для опытного iOS-инженера / техлида. Цель — уверенно проходить секцию System Design / Mobile Architecture в Яндекс, Avito, Ozon, VK — на уровне senior и техлид/staff.

5–6 недель7 кейсов data flow > UImobile-specific

Акцент: мобильная специфика (lifecycle, память, батарея, офлайн, сеть) и data flow, а не UI/фреймворки. Главный принцип — «измерь дважды, отрежь один раз» (Jacob's Tech Tavern, 2026).

Модуль 0 · Что оценивают на уровне техлида

В отличие от IC, от лида/staff ждут системного мышления (System Design Newsletter):

  • Senior проактивно вскрывать скрытые требования, обосновывать архитектуру (repository pattern для offline-first), глубокое знание mobile-specific, адаптация при смене требований, вести разговор.
  • Staff/Lead фреймить проблему («а нужен ли офлайн вообще?»), думать о структуре команды и владении модулями, проектировать под будущее, системные трейд-оффы (модуляризация, build time, деплой, обратная совместимость).

В РФ для лида добавляется управленческо-продуктовый разрез: «как спроектировать систему, выдерживающую ×10 нагрузки за год?», «два сеньора спорят про подход — как решите?» (Яндекс Практикум).

Материалы модуля

Модуль 1 · Универсальный фреймворк ответа

flowchart LR A["1. Requirements
& Scope
5-10 мин"] --> B["2. API &
Data needs
5-10 мин"] B --> C["3. High-level
Architecture
10-15 мин"] C --> D["4. Deep Dive
15-20 мин"] D --> E["5. Wrap-up
1-5 мин"] E -. "адаптация при смене требований" .-> C
  1. Requirements & Scope (5–10 мин) — что проектируем + ограничения.
  2. API & Data needs (5–10 мин) — что приложению нужно от сервера; data model.
  3. High-level architecture (10–15 мин) — клиентские компоненты + поток данных.
  4. Deep dive (15–20 мин) — один критичный флоу / адаптация под новое требование.
  5. Wrap-up (1–5 мин) — резюме решений и трейд-оффов.

Reusable-паттерн на каждом шаге: задать 2–3 высокоэффективных вопроса → переформулировать и получить «да» → записать решения/ограничения → рано подсветить red flags → не обязательно решать всё, показать осознанный трейд-офф.

Модуль 2 · Сбор требований

Уточняющие вопросы:

  • Что = успех фичи? (скорость/надёжность/простота) Что одно важнее всего?
  • Всё приложение или часть? Платформы (iOS only / cross-platform)?
  • Сетевые условия (всегда онлайн / нестабильно / офлайн)?
  • Кто пользователи, какие устройства (флагманы / low-end)?
  • Технические ограничения (легаси, существующая авторизация)?
Red flags, подсветить рано: размытые требования («сделай быстро»), противоречия («просто, но кастомизируемо»), отсутствие данных об офлайне/авторизации/источнике данных.

Модуль 3 · Базовые концепты систем

Клиент-сервер, REST/GraphQL/gRPC/WebSockets, БД (SQL/NoSQL), индексы, репликация/шардирование, очереди, балансировка, CAP, согласованность.

Модуль 4 · Кэширование и офлайн (mobile-critical)

Уровни кэша (memory/disk/URLCache), стратегии (cache-aside, write-through, write-back), инвалидация, TTL; офлайн-first, синхронизация и разрешение конфликтов (last-write-wins → CRDT), пре-фетч.

flowchart LR REQ["Request"] --> CHK{"В кэше?"} CHK -- "да, свежо" --> RET["Вернуть из кэша"] CHK -- "нет/устарело" --> SRC["Источник / сеть"] SRC --> UPD["Обновить кэш
TTL"] UPD --> RET

Модуль 5 · Клиентская архитектура iOS

Слои (UI / Presentation / Domain / Data / Network), repository pattern, DI, модуляризация (SPM-модули, владение командами), feature flags / dynamic delivery, навигация/координаторы. Mobile-specific: app lifecycle, memory pressure, battery, background tasks, фоновые загрузки с чанкингом (напр. 50MB видео).

flowchart TB UI["UI Layer
SwiftUI / UIKit"] --> P["Presentation
ViewModel / Presenter"] P --> DOM["Domain
Use Cases"] DOM --> REPO["Repository"] REPO --> NET["Network
URLSession"] REPO --> CACHE[("Local Cache
memory + disk")] NET --> API["Backend API"] CACHE -. "offline-first" .-> REPO

Модуль 6 · Разбор кейсов (ядро курса)

Для каждого кейса проходим фреймворк целиком и фиксируем mobile-специфику.

Кейс 6.1 — Лента (Feed) ★★★ · полный разбор

Пагинация, медиа-тяжёлый контент, кэш картинок/видео, пре-фетч, pull-to-refresh, офлайн ранее загруженного. Скрытые требования: лимит трафика, low-end устройства.

  1. Требования. Функциональные: бесконечная лента постов (текст + медиа), пагинация, pull-to-refresh, лайк/коммент, просмотр офлайн ранее загруженного. Нефункциональные: плавный скролл 60 fps, экономия трафика и батареи, low-end устройства, быстрый «холодный старт» (мгновенно из кэша). Скрытые (вскрыть самому): лимит трафика, поведение при потере сети, ранжирование на сервере vs клиенте.
  2. API / контракт. GET /feed?cursor=<opaque>&limit=20 → массив постов + next_cursor. Курсорная пагинация, а не offset — offset «плывёт» при вставке новых постов сверху. Медиа — ссылки на CDN с несколькими разрешениями (thumbnail/medium/full), клиент выбирает по экрану и сети. ETag/If-None-Match для дешёвого refresh.
  3. Архитектура клиента. Repository как единый источник правды: сначала отдаёт данные из локальной БД (мгновенный показ), параллельно идёт сетевой запрос, результат мёржится и обновляет UI. Слои: Network → Repository (+ кэш-политика) → ViewModel → View. Медиа — отдельный image-loader с дисковым + memory-кэшем (NSCache + диск), отменой запросов при переиспользовании ячейки и пре-фетчем (UICollectionViewDataSourcePrefetching).
  4. Deep dive — производительность скролла. Переиспользование ячеек, асинхронная декодировка изображений вне главного потока, предрасчитанные высоты ячеек, отмена загрузки при быстром скролле, downsampling больших картинок до размера ячейки (иначе memory pressure → Jetsam). Пре-фетч следующей страницы за 5 ячеек до конца.
  5. Офлайн и синхронизация. Кэшируем N последних страниц в БД + медиа на диск с LRU-эвикцией по лимиту размера. Лайки офлайн — оптимистичный апдейт + очередь исходящих действий с повтором при появлении сети.
  6. Trade-offs (staff-разрез). Push (fan-out на запись — быстрый GET, дорогая запись для «звёзд» с млн подписчиков) vs Pull (fan-out на чтение — дёшево писать, дорого читать) vs гибрид (для «звёзд» pull, остальным push). На клиенте: агрессивный пре-фетч экономит время, но жжёт трафик/батарею. Где ранжировать: сервер (единая логика, A/B) vs клиент (персонализация офлайн).
flowchart TB subgraph Client["iOS Client"] V["Feed View
pagination"] --> VM["FeedViewModel"] VM --> FR["Feed Repository"] FR --> MEM[("Memory cache")] FR --> DISK[("Disk cache
offline")] FR --> IMG["Image cache
+ prefetch"] end FR --> GW["API Gateway"] GW --> FS["Feed Service"] FS --> DB[("Posts DB")] FS --> CDN["CDN
media"] IMG --> CDN

Кейс 6.2 — Чат / Мессенджер ★★★ · полный разбор

Real-time (WebSocket/long-poll), доставка/прочтение, очередь исходящих, локальная БД, синхронизация, E2E-шифрование, масштаб до 1 млрд.

  1. Требования. Функциональные: 1:1 и групповые чаты, доставка в реальном времени, статусы (отправлено/доставлено/прочитано), «печатает», медиа-вложения, история и поиск. Нефункциональные: низкая задержка, гарантия доставки и порядка, работа при нестабильной сети, масштаб до ≈ 1 млрд пользователей. Скрытые: E2E-шифрование, мульти-девайс синхронизация, поведение в фоне/push.
  2. Транспорт. Постоянный WebSocket для двустороннего обмена (fallback long-poll), APNs для доставки, когда сокет закрыт (фон/убитое приложение). При возврате онлайн — переподключение с backoff и delta-sync по курсору.
  3. Модель доставки и порядок. Каждое сообщение получает клиентский clientMessageId (UUID) для идемпотентности (ретраи не плодят дубли) и серверный монотонный seq/timestamp для порядка. Порядок определяет сервер, а не часы устройства (они расходятся).
  4. Архитектура клиента и очередь исходящих. Локальная БД (SQLite/Core Data/GRDB) — источник правды для UI. Отправка: сообщение сразу пишется в БД со статусом pending (оптимистичный UI), затем фоновый «отправитель» берёт из очереди и шлёт в сокет; при ACK → sent, при ошибке → ретрай с backoff. Входящие дедуплицируются по id и вставляются по seq.
  5. Синхронизация и мульти-девайс. При подключении клиент посылает последний известный seq по каждому чату → сервер отдаёт только дельту. Непрочитанные и статусы хранятся на сервере, чтобы совпадали на всех устройствах.
  6. Trade-offs (staff-разрез). at-least-once + дедупликация по id даёт эффект exactly-once без дорогих распределённых транзакций. E2E (Signal-протокол) усиливает приватность, но ломает серверный поиск/антиспам и усложняет мульти-девайс (раздача ключей). Постоянные WebSocket-соединения дороги по памяти на сервере — баланс с push-only для неактивных.
flowchart TB subgraph Client["iOS Client"] CV["Chat View"] --> CVM["ChatViewModel"] CVM --> OUTQ["Outbox queue
retry/idempotency"] CVM --> LDB[("Local DB
messages")] CVM --> WS["WebSocket client"] end WS <--> CG["Chat Gateway
WebSocket"] CG --> MS["Message Service"] MS --> MQ[["Message Queue"]] MS --> MDB[("Messages DB")] MS --> PUSH["Push Notif
APNs"] OUTQ -. "resend on reconnect" .-> WS

Кейс 6.3 — Музыкальный / медиа-стриминг ★★★

Стриминг, буферизация, офлайн-загрузки, плейлисты, фоновое воспроизведение, кодеки.

Кейс 6.4 — Видеохостинг (YouTube-like) ★★★

Загрузка/транскодинг, CDN, адаптивный битрейт, лента рекомендаций.

Кейс 6.5 — Ride-sharing (Uber-like) ★★★ · полный разбор

Гео, real-time tracking, матчинг, карты, состояние поездки.

  1. Требования. Функциональные: запрос поездки, матчинг с ближайшим водителем, real-time трекинг машины на карте, расчёт ETA/цены, состояния поездки (поиск → принята → в пути → завершена), оплата. Нефункциональные: низкая задержка матчинга, точность гео, отказоустойчивость при потере сети в движении, экономия батареи (постоянный GPS — главный потребитель). Скрытые: surge-ценообразование, отмена с обеих сторон, поведение в туннеле/без сигнала.
  2. API / контракт. Гео-апдейты водителя — частый, но компактный поток (батчинг локаций, бинарный формат). POST /rides (идемпотентно по request-id), затем подписка на статус поездки. Пассажир получает позицию водителя через push по сокету; интерполяция движения между апдейтами для плавности.
  3. Архитектура клиента. Конечный автомат поездки (explicit state machine) — единственный источник истины состояния, переходы только по событиям с сервера. Слой локации абстрагирован: разная частота GPS по состоянию (редко в ожидании, часто в поездке) для экономии батареи. Карта рендерит позиции через слой аннотаций с анимацией перемещения.
  4. Deep dive — гео и матчинг. На сервере геоиндекс (geohash / S2 / quadtree) для запроса «ближайшие водители в радиусе». Матчинг учитывает дистанцию, ETA, рейтинг, направление. На клиенте: throttling гео-апдейтов, отправка только при значимом смещении, сжатие трека. ETA считается на сервере (актуальный трафик), клиент показывает кэш до обновления.
  5. Офлайн и надёжность. Состояние поездки кэшируется локально: при кратком пропадании сети UI остаётся консистентным, апдейты буферизуются и досылаются. Запрос поездки идемпотентен (request-id) — повтор при таймауте не создаёт две поездки. Оплата — отдельный идемпотентный шаг с подтверждением.
  6. Trade-offs (staff-разрез). Частота GPS: точность трекинга против расхода батареи — адаптивная частота по состоянию и скорости. Матчинг на сервере (глобальная оптимизация, surge) против гибридного (клиент предлагает кандидатов). Геоиндекс: точность против стоимости пересчёта при высокой плотности апдейтов. Push через сокет (мгновенно, дорого держать) против polling (проще, выше задержка).
flowchart TB subgraph Rider["iOS Rider App"] RV["Map View"] --> RVM["RideViewModel"] RVM --> LOC["Location stream"] RVM --> RWS["WebSocket"] end subgraph Driver["iOS Driver App"] DLOC["Location publisher"] end RWS <--> GS["Geo / Match Service"] DLOC --> GS GS --> GEO[("Geo index
quadtree/geohash")] GS --> MATCH["Matching engine"] GS --> TRIP[("Trip state DB")] GS --> MAPS["Maps / Routing"]

Кейс 6.6 — Финтех / банковское моб. приложение ★★★

Безопасность, secure storage, авторизация, идемпотентность платежей, офлайн-ограничения.

Кейс 6.7 — Дизайн-система / SDK ★★★

Версионирование, обратная совместимость, распространение (SPM), API-контракты — частый вопрос для staff.

Модуль 7 · Мок-интервью

Смотрите чужие моки, затем проводите свои с партнёром.

Чек-лист на интервью (mental checklist)

flowchart LR U["Users"] --> L["Load"] --> AR["Architecture"] --> AP["API"] --> S["State"] --> O["Offline"] --> SE["Security"] --> M["Monitoring"]

Mobile-болевые точки, которые отличают сильного кандидата: нет сигнала? поворот экрана в середине запроса? фоновый режим и session drift? memory pressure? батарея? (System Design Handbook).

Недельный план (5–6 недель)

НеделяМодулиКейсыМок
1M0–M2 (уровни, фреймворк, требования)
2M3–M4 (концепты, кэш/офлайн)6.1 Лента
3M5 (клиентская архитектура iOS)6.2 Чат1
4M6 кейсы6.3, 6.41
5M6 кейсы6.5, 6.6, 6.72
6M7 мок-марафон + слабые местаповтор 2 кейсов3+

Критерии готовности

0 из 5 · 0%

Бесплатный старт: weeeBox/mobile-system-designthemobileinterview.com → FaangTalk. Платно: «Building Mobile Apps at Scale» (G. Orosz) + Educative «Grokking the Mobile System Design Interview». Подробнее — на странице Ресурсы.