Человек отвечает на тест в браузере, закрывает ноутбук и через минуту открывает приложение. Вместо продолжения система показывает главную. Курс, тест и место остановки приходится искать заново. Формально аккаунт тот же; для пользователя прогресс пропал.
Привет, я — Антон Фокин, CEO Qtim. Мы разрабатываем веб-сервисы, мобильные приложения и EdTech-платформы. На оценке мы чаще всего видим требование в одну строку: «синхронизировать данные между вебом и приложением». За этой строкой обычно нет ответа, что показать на другом устройстве и что считать подтверждённым действием.
На проектах мы видим пять мест потери контекста: локальное состояние остаётся на устройстве; веб и приложение работают с разными версиями записи; запрос повторяется после обрыва связи; фоновая операция остаётся без статуса; после возврата клиент не восстанавливает предыдущий шаг. Первым обычно пропадает текущий шаг: вход срабатывает, а нужный экран, черновик или статус приходится восстанавливать заново. Если разбирать это после прототипа, синхронизация быстро получает отдельный бэклог. На макете она по-прежнему занимает одну стрелку.
Авторизация сообщает, кто вошёл. Чтобы продолжить задачу, система хранит роль, открытый объект, этап процесса, прогресс, черновик, версию данных и статус последнего действия.
Возьмём учебный тест. Запись «пользователь 42 авторизован» почти бесполезна без номера попытки, ответов, оставшегося времени и правил повторного входа. Если проверка файла идёт в фоне, к контексту добавляется статус: работа принята, обрабатывается, проверена или требует повторной загрузки.
Даже знакомое поле может оказаться спорным. Таймер, сохранённый на телефоне как «12 минут», устареет, пока устройство лежит без сети. Надёжнее хранить на сервере время начала и дедлайн, а на каждом клиенте заново рассчитывать остаток. Тогда часы на ноутбуке и смартфоне не проводят собственное совещание.
У разных сценариев свой состав состояния. В доставке это активный заказ, маршрут и изменения, сделанные без сети. В личном кабинете — выбранная организация, права и незавершённая заявка. В семейном аккаунте — профиль ребёнка, который родитель открыл перед тем, как свернуть приложение.
Это требование мы раскладываем на сущности и действия, которые человек продолжит на другом устройстве. Чаще всего мобильная и серверная команды расходятся именно здесь: первая описывает текущий экран, вторая — последнюю сохранённую запись. У каждой записи поэтому появляются владелец, версия и правило обновления.
Веб и приложения должны сверяться с одним источником. Обычно это сервер: iOS, Android, браузер и внутренние сервисы получают через API одну модель состояния и восстанавливают из неё текущий шаг.
Телефон при этом сохраняет локальный кеш, черновики, очередь действий и данные для работы без сети. Интерфейс читает локальную копию сразу, а затем сверяет её с сервером. Документация Android разделяет локальный и сетевой источники и отдельно описывает синхронизацию после восстановления связи. Для продукта это означает: локальная копия ускоряет экран, но не становится вторым источником истины.
Граница проходит по сроку жизни данных:
Общий контракт описывает поля ответа и переходы между состояниями: идентификатор попытки, версию записи, условие завершения шага и права клиента на обновление. Иначе веб и приложение получают данные из одного места, но трактуют их по-разному.
Ссылки из уведомлений тоже входят в эту модель. В ссылке можно передать идентификатор заказа, урока или сообщения, чтобы человек сразу попал в нужное место. Сервер при открытии заново проверяет роль и доступ. Старое уведомление не должно становиться пропуском к данным, которые пользователь уже не вправе видеть.
Если веб и мобильные клиенты расходятся по статусам или правам, посмотрите, как в Qtim устроена разработка SaaS-продуктов.
Мобильная сеть создаёт дубли. Пользователь нажал «Отправить», ответ не пришёл, кнопка осталась активной. Он нажал ещё раз. Затем приложение снова отправило запрос после восстановления связи. На сервер приехали три одинаковые сдачи, хотя пользователь совершил одно действие.
Для таких действий нужен идентификатор операции. Клиент создаёт его до отправки и использует при повторных попытках. Сервер видит, что действие уже принято, и возвращает прежний результат вместо нового списания, заявки или домашней работы.
Стандарт RFC 9110 называет HTTP-метод идемпотентным, если несколько одинаковых запросов дают тот же ожидаемый эффект, что и один. Для запросов через POST это свойство часто приходится проектировать отдельно: хранить ключ операции, результат и время его действия.
Пользователю при этом нужен понятный статус. «Принято, обрабатываем» лучше бесконечной загрузки. Если ответ потерялся по дороге, другое устройство запрашивает состояние операции по этому ключу.
Без сети устройство какое-то время живёт со своей копией данных. Пользователь меняет заявку в приложении, а сотрудник обновляет ту же запись в вебе. После подключения на сервер приходят две версии. Синхронизация состоялась, но серверу всё равно нужно решить, какая версия допустима.
Политику выбирают по типу данных. Независимые поля заявки можно объединить, права доступа сервер пересчитывает по актуальной роли, а два изменения одного поля требуют проверки. Одно правило здесь опасно: конфликт в переключателе и потеря черновика имеют разную цену.
Для этого у записи должна быть версия, а у действия — время и автор. Получив устаревшую версию, сервер отклоняет изменение или возвращает конфликт в интерфейс. Политику разрешения конфликтов задают до запуска офлайн-режима, особенно для черновиков и необратимых действий.
Контекст теряется и без перехода на другой экран. Пользователь отправил файл, сервер принял запрос и начал обработку. Если интерфейс показывает только два состояния — «до отправки» и «готово», несколько минут посередине выглядят как сбой.
Фоновая обработка продолжается после закрытия вкладки. Сервер хранит промежуточный статус рядом с операцией: запрос принят, обрабатывается, завершён или остановлен с ошибкой. После повторного входа клиент запрашивает этот статус и продолжает с него.
Интерфейс должен различать три ситуации. Если запрос не дошёл, действие можно отправить снова. Если обработка задержалась, экран показывает текущий статус. Если операция завершилась с ошибкой, человек получает объяснение и следующий шаг. Одна кнопка «попробовать ещё раз» не заменяет эту модель.
Перед оценкой проходим один маршрут целиком: открываем заявку, загрузку файла или учебную попытку на ноутбуке и телефоне, меняем с обоих устройств, отключаем сеть перед отправкой и повторяем запрос после подключения.
Проверка пройдена, если:
Если хотя бы один пункт не выполняется, проблема затрагивает схему данных, правила обновления или обработку запросов.
Если у продукта есть веб-версия и вы планируете мобильный клиент, команда Qtim может оценить готовность архитектуры к разработке приложения: определить общий контур состояния, границы локальных данных и сценарии возврата после сбоя.