Connect with us

Разработка

Собеседование в Zerodha — эти вопросы полностью изменили мое представление о мобильной разработке

Этот опыт заставил меня иначе взглянуть на мобильную разработку

Опубликовано

/

     
     

Прежде чем приступить к интервью, важно понять, в чём заключается суть проекта.

Zerodha не создаёт социальную ленту или приложение для заметок. Они создают:

  • Торговую платформу в реальном времени
  • Систему, обрабатывающую тысячи обновлений цен
  • Приложение, в котором пользователи размещают срочные финансовые ордера
  • Продукт, используемый миллионами активных трейдеров

Когда пользователь нажимает «Купить», он не запускает безобидное действие интерфейса. Он принимает финансовое решение. Этот контекст меняет всё.

Собеседование в Zerodha - эти вопросы полностью изменили мое представление о мобильной разработке

Во время интервью я понял, что в приложении для торговли акциями:

  • Важна задержка
  • Важна точность
  • Важна стабильность
  • Важны сессии без сбоев
  • Важна синхронизация состояния

Это не декоративная мобильная разработка. Это мышление на уровне инфраструктуры, применённое к клиентской стороне.

Первый раунд: 10 000 обновлений цен в секунду

Один из первых вопросов, который застал меня врасплох, касался обработки высокочастотных обновлений данных.

Они спросили:

Если акции очень волатильны, и вы получаете тысячи обновлений цен в секунду через WebSocket, как обновить пользовательский интерфейс, не заморозив его?

Это не теоретический вопрос. На торгуемых акциях потоки цен могут обновляться чрезвычайно часто.

Моим первоначальным инстинктом было говорить об обновлении пользовательского интерфейса в основном потоке и обеспечении того, чтобы ресурсоемкие процессы оставались в фоновых потоках. Это базовые знания.

Но более глубокая проблема не в многопоточности.

Настоящая проблема — это обратное давление.

Если вы слепо отображаете каждое входящее обновление, ваш пользовательский интерфейс будет «задыхаться». Основной поток будет постоянно перерисовывать метки или представления SwiftUI, и выпадение кадров станет неизбежным.

Лучший подход — контролировать поток обновлений.

Например, в Swift с использованием Combine:

webSocketPublisher
    .receive(on: DispatchQueue.global(qos: .userInitiated))
    .map { $0.price }
    .removeDuplicates()
    .throttle(for: .milliseconds(200), scheduler: DispatchQueue.main, latest: true)
    .sink { [weak self] latestPrice in
        self?.priceLabel.text = "\(latestPrice)"
    }

Не нужно показывать каждое микроизменение. Необходимо показывать последнее значимое состояние.

Этот вопрос заставил меня задуматься о:

  • Ограничении скорости передачи данных
  • Стабильности пользовательского интерфейса под нагрузкой
  • Значимой отрисовке вместо реактивного хаоса

Мобильные приложения часто рассматриваются как «простые клиенты». Но в финансовом приложении реального времени ваш клиент должен вести себя разумно.

WebSocket против поллинга: это не просто сетевой выбор

Еще одно обсуждение касалось потоков данных в реальном времени.

Они спросили о разнице между WebSocket и polling, и, что более важно, когда каждый из них имеет смысл в биржевом приложении.

Опрос каждые несколько секунд прост, но он вносит:

  • Задержку
  • Накладные расходы на сеть
  • Потенциальную несогласованность

WebSocket обеспечивают постоянное соединение и отправляют обновления в реальном времени. Но они также вносят:

  • Сложность управления соединением
  • Логику повторного подключения
  • Восстановление состояния
  • Краевые случаи при обрывах сети

Настоящая проблема заключается не в выборе WebSocket. Она заключается в обработке нестабильности.

Они спросили:

Что произойдет, если WebSocket отключится сразу после того, как пользователь разместит заказ?

Этот вопрос полностью изменил направление обсуждения.

Теперь речь шла о надежности.

Вам потребуется:

  • Идемпотентность запроса
  • Отслеживание подтверждения на стороне сервера
  • Логика повторных попыток
  • Локальное временное состояние до подтверждения

Например:

struct OrderRequest: Codable {
    let orderId: UUID
    let stockSymbol: String
    let quantity: Int
    let price: Double
}

Каждый ордер должен иметь уникальный идентификатор. Если сеть обрывается и клиент пытается повторно, сервер должен распознавать дублирующие запросы и избегать размещения дублирующих сделок.

И это уже не просто мобильная логика — это мышление в терминах распределённых систем, применённое к приложению на телефоне.

Разработка экрана размещения заказов в реальном времени

Один из раундов был полностью посвящён системному дизайну с точки зрения мобильной разработки.

Меня попросили спроектировать экран живого биржевого стакана (order book), который отображает:

  • заявки на покупку (Buy orders)
  • заявки на продажу (Sell orders)
  • изменения цен в реальном времени
  • изменения объёма торгов

На первый взгляд это звучит как что-то простое — обычный UITableView или List в SwiftUI.

Но когда обновления поступают непрерывно, наивные реализации приводят к:

  • Мерцанию интерфейса (flickering)
  • Постоянным перезагрузкам
  • Высокому использованию ЦП
  • Скачкам потребления памяти

Правильный подход — использовать обновления на основе diff-алгоритма (diff-based updates), чтобы изменять только действительно обновившиеся элементы, а не перерисовывать весь список целиком.

Вместо перезагрузки всей таблицы:

  • Сравнение предыдущего состояния и нового состояния
  • Вычисление минимальных изменений
  • Применение операций вставки/удаления/обновления

В UIKit:

tableView.performBatchUpdates {
    tableView.insertRows(at: newIndexPaths, with: .automatic)
    tableView.deleteRows(at: removedIndexPaths, with: .automatic)
}

В SwiftUI вы полагаетесь на стабильную идентификацию:

ForEach(orderBookEntries, id: \.id) { entry in
    OrderRowView(entry: entry)
}

Но идентичность должна быть стабильной. Если ваша модель без необходимости пересоздает объекты, SwiftUI будет перерисовывать всё.

Интервью заставило меня осознать нечто важное. В высокочастотных финансовых экранах моделирование данных важнее, чем дизайн пользовательского интерфейса.

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

Ещё один раунд был сильно сфокусирован на конкурентности (concurrency).

Они спросили:

Как обеспечить потокобезопасность, если несколько потоков обновляют одно и то же состояние?

В торговом приложении могут быть:

  • Обновления WebSocket
  • REST вызовы обновления
  • Действия, инициированные пользователем
  • Фоновая синхронизация

Если всё это изменяет общее состояние, возникают реальные состояния гонки.

Используя Swift Concurrency, вы можете инкапсулировать состояние внутри актора:

actor OrderStore {
    private var orders: [UUID: Order] = [:]
func update(order: Order) {
        orders[order.id] = order
    }
    func fetch(id: UUID) -> Order? {
        orders[id]
    }
}

Акторы обеспечивают изоляцию. Только одна задача может одновременно получать доступ к внутреннему состоянию.

Но тогда возникают компромиссы в производительности.

Если всё изолировано акторами, вы можете ненужно сериализовать операции. Необходимо понимать, где изоляция необходима, а где достаточно неизменяемости.

Этот уровень вопросов больше напоминал бэкенд-разработку, чем мобильную.

Вопрос, который остался со мной

Один вопрос действительно до сих пор не дает мне покоя:

Как гарантировать, что пользовательский интерфейс всегда будет отражать истину сервера, а не предположение клиента?

Звучит просто. Но это не так.

Представьте, что пользователь размещает ордер на покупку. В пользовательском интерфейсе сразу отображается «Ордер размещен». Но сервер отклоняет его из-за недостаточной маржи. Если вы оптимистично обновляете пользовательский интерфейс без согласования, вы вводите пользователей в заблуждение. И вот здесь начинаются настоящие вопросы к архитектуре и работе с асинхронностью.

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

  • Временное оптимистичное состояние
  • Индикатор состояния ожидания
  • Четкая обработка подтверждения сервера
  • Явный откат ошибки

Что-то вроде:

enum OrderState {
    case pending
    case confirmed
    case rejected(String)
}

Ваш пользовательский интерфейс должен быть управляемым состоянием, а не событиями.

Этот вопрос изменил мое представление об архитектуре пользовательского интерфейса. Пользовательский интерфейс не должен предполагать успех. Он должен реагировать на подтвержденные переходы состояний.

Автономный режим в торговом приложении

Они также спрашивали о поведении в автономном режиме.

В финансовом приложении автономный режим — опасная территория.

Следует ли разрешать размещение ордеров в автономном режиме? Вероятно, нет. Следует ли кэшировать последние известные цены? Да, но четко помечайте их как устаревшие.

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

Например:

struct StockPrice {
    let value: Double
    let timestamp: Date
}

А в UI, если таймстемп старше допустимого порога, нужно показать предупреждение: «Данные могут быть устаревшими».

Именно такие небольшие UX-решения формируют доверие.

Тишина после всех раундов

После завершения всех этапов я чувствовал удовлетворение. Обсуждения были содержательными. Я многому научился. В чём-то показал себя отлично, в чём-то — не идеально.

А потом наступила тишина.

Ни отказа. Ни оффера. Просто ожидание.

Раньше в карьере это сильно бы меня задело. Сейчас я смотрю на интервью иначе.

Иногда:

  • происходит заморозка найма
  • меняются приоритеты роли
  • выбирают внутреннего кандидата
  • пересматривается бюджет

Интервью — это не всегда чистая проверка навыков. Это ещё и бизнес-решения.

Но полученный опыт остаётся навсегда.

Что изменило во мне это интервью

Этот опыт заставил меня иначе взглянуть на мобильную разработку.

Во-первых, мобильные приложения больше нельзя воспринимать как «тонкий UI-слой». В высокорисковых доменах вроде финансов клиент — это часть распределённой системы.

Во-вторых, управление состоянием — это не вопрос вкуса в архитектуре. Это требование корректности.

В-третьих, конкурентность — не продвинутая тема «на вырост». Это обязательная база.

В-четвёртых, производительность — это не про плавные анимации. Это про устойчивость под нагрузкой.

И наконец, точность имеет значение. В трейдинге ошибка округления — это не косметический баг. Это реальные деньги.

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: