Offline-first мобильные приложения: правила синхронизации
Offline-first мобильные приложения требуют понятных правил синхронизации. Разберем LWW, слияние, тексты в UI и частые ошибки.

Что не так с офлайн-режимом, если правила не объяснить
Офлайн-режим почти всегда воспринимают как простое обещание: «приложение работает, даже если сети нет». Пользователь ожидает, что можно продолжать привычные действия, а потом все аккуратно синхронизируется. Так обычно и представляют себе offline-first мобильные приложения.
Проблема в том, что без понятных правил это обещание быстро ломается о реальность.
Типичная сцена: метро, самолет, плохой Wi‑Fi в кафе, переключение SIM или внезапный «E» вместо 4G. Приложение то подключается, то нет, и любая неясность превращается в тревогу. Если человек не понимает, что именно сохраняется, где находится «правда» и что будет при возвращении сети, он начинает действовать наугад: жмет «Сохранить» по десять раз, закрывает приложение, делает скриншоты, копирует текст в заметки.
Главный страх почти всегда один: «я потеряю изменения». Он появляется не потому, что синхронизация обязательно плохая, а потому что интерфейс молчит. Если кнопка «Сохранить» выглядит одинаково онлайн и офлайн, а статус никак не меняется, пользователь не отличает три разных состояния:
- сохранено на устройстве,
- отправлено на сервер,
- подтверждено сервером.
Ожидания при этом простые: если текст виден на экране, значит он надежно сохранен; когда связь вернется, ничего не исчезнет и не «откатится»; если редактирование было с двух устройств, приложение «само разберется»; если случилась ошибка, ему скажут простыми словами, что делать.
Когда эти ожидания не совпадают с реальными правилами синка, разочарование звучит как «приложение глючит», хотя технически оно может работать корректно. Просто правила скрыты.
Отдельная ловушка - обещания в интерфейсе. Есть формулировки, которые можно давать смело, потому что они проверяемые и честные. А есть те, которые лучше не использовать, пока вы не уверены на 100%.
Хорошие обещания (если вы реально так делаете): «Сохранено на устройстве», «Отправим, когда появится сеть», «Последняя синхронизация: 12:40» (именно время подтверждения), «Есть несинхронизированные изменения» (и пользователь может их увидеть).
Опасные обещания: «Все данные сохранены» (если это только локально и без уточнений), «Синхронизировано» (если нет подтверждения сервера), «Ничего не потеряется» (если возможны конфликты и перезаписи).
Если вы делаете приложение, например, в TakProsto, полезно сразу договориться о словаре состояний: что значит «сохранено», что значит «синхронизировано» и где именно пользователь это видит. Тогда офлайн-режим перестает быть загадкой и начинает ощущаться как надежная функция, а не лотерея.
Что делать офлайн: границы и приоритеты
Офлайн-режим работает лучше всего, когда у него есть честные границы. Пользователь должен понимать: что точно доступно без сети, что попадет в очередь, а что временно недоступно. Если правила размыты, люди начинают «проверять» приложение случайными действиями и теряют доверие.
Базовый набор, который обычно нужен почти всем: просмотр уже загруженных данных, создание новых сущностей и редактирование того, что было открыто ранее. Это и есть ключевое обещание offline-first: работа не останавливается, даже если сеть пропала в лифте или метро.
При этом часть функций безопаснее отложить до онлайна. Не потому, что «не получилось», а потому что так проще объяснить и легче гарантировать результат. Чаще всего в «только онлайн» отправляют тяжелые файлы (фото, видео, большие документы), глобальный поиск по всем данным (если без сервера он будет неполным или медленным), фоновые пересчеты и рекомендации, а также справочники, которые редко нужны здесь и сейчас и быстро устаревают.
Дальше определите приоритеты очереди. Когда связь появится, синхронизация не должна начинать с самого тяжелого. Лучше сначала отправить то, что влияет на ощущение завершенности: созданные записи, изменения статусов, комментарии, короткие поля формы. Файлы, превью и прочие «украшательства» можно догружать позже или только по Wi‑Fi.
Важно заранее договориться, что именно синхронизируется как одна «единица данных». Это снижает сюрпризы и упрощает правила синхронизации данных. Единицей может быть карточка товара, заказ, заметка, задача, чат с сообщениями. Хорошее правило: единица должна быть понятной пользователю и иметь ясную границу «сделано / не сделано». Например, «заказ» синхронизируется целиком (позиции, адрес, статус), а не отдельными полями вразнобой.
Проверьте это на простом сценарии: пользователь без сети создает заказ и меняет в нем статус. Если единица определена правильно, вы можете честно сказать: «Заказ сохранен на устройстве. Как появится сеть, он будет отправлен». Если единицы нет, придется объяснять, почему статус ушел, а адрес нет, и это почти всегда выглядит как ошибка.
Если вы собираете приложение через TakProsto, полезно зафиксировать границы в planning mode: какие экраны работают офлайн, какие действия ставятся в очередь и в каком порядке они уходят на сервер. Это экономит время на переделках, когда UX уже нарисован, а синк еще не продуман.
Как подготовить данные к синхронизации без сюрпризов
Большинство проблем с офлайн-синхронизацией появляются не в «момент синка», а раньше - в структуре данных. Если записи нельзя однозначно опознать, понять порядок изменений и аккуратно обработать удаление, пользователь увидит дубли, «откаты» и странные прыжки статуса.
Локальные идентификаторы: чтобы не плодить дубли
В офлайне приложение должно уметь создавать объекты сразу, не ожидая ответа сервера. Для этого нужен локальный ID, который гарантированно уникален. Частая ошибка - временный числовой ID (1, 2, 3), который потом совпадает с чужими данными или перезаписывается.
Практичное правило: создавайте запись с постоянным уникальным локальным идентификатором и храните отдельное поле для серверного ID (которое появится после синхронизации). Тогда при синке вы просто «прикрепляете» server_id, а не создаете новый объект заново.
Пример: пользователь создал задачу в метро. У нее есть local_id сразу. Когда сеть появится, сервер вернет server_id, и вы обновите запись, а не добавите вторую «такую же» задачу.
Версионирование: как понять, что новее
Синк должен понимать, какая версия записи актуальна. Здесь помогает простая дисциплина: у каждой записи есть версия и метка изменения.
- version: счетчик, который увеличивается при каждом локальном изменении
- updated_at: время изменения (полезно для логов и понятных сообщений)
- updated_by: кто изменил (пользователь, устройство, сервис)
Не полагайтесь только на время. Часы могут быть неверными, а два устройства могут дать одинаковые отметки. Версия или серверный порядок операций обычно надежнее.
Журнал изменений: чтобы можно было «переиграть»
Вместо того чтобы отправлять на сервер «текущее состояние всего», храните очередь операций: что именно сделал пользователь. Это помогает повторить действия на сервере в том же порядке и точнее разрулить конфликты.
Достаточно записывать: тип операции (создать, изменить, удалить), идентификатор объекта, измененные поля и версию. Если синк упал на середине, вы не теряете правду: просто повторяете неотправленные операции.
Удаления: мягкое удаление проще
Удаление в офлайне часто ломает синк, потому что «записи больше нет». Поэтому мягкое удаление обычно безопаснее: храните флаг is_deleted и время удаления. Тогда удаление тоже становится обычной операцией в журнале.
Пользовательский плюс тоже заметный: можно показать «Удалено, будет удалено на всех устройствах после синхронизации» и при желании дать короткий период на отмену.
Разрешение конфликтов: LWW, merge и ручной выбор
Когда устройство было офлайн, а потом вернулось в сеть, вы почти неизбежно сталкиваетесь с конфликтами: один и тот же объект успели изменить в двух местах. В offline-first мобильных приложениях важно выбрать правило, которое пользователь сможет предсказать.
Три подхода, которые реально работают
LWW (last-write-wins, «последняя запись победила») подходит там, где цена ошибки низкая, а «самое свежее» обычно и есть правильное. Но у LWW есть минус: можно незаметно потерять изменения, сделанные на другом устройстве.
Merge (слияние) подходит для полей, которые можно безопасно объединить. Это не магия, а набор понятных правил: что складываем, что объединяем, что оставляем уникальным. Слияние хорошо работает для списков и независимых полей, но плохо для текста, где важны порядок и смысл.
Ручное разрешение нужно там, где автоматическое решение может привести к дорогой ошибке или спорной ситуации. Да, это добавляет шаг, но снимает риск тихо испортить данные.
На практике удобнее думать не «какое правило у приложения», а «какое правило у каждого типа данных»:
- Заметка (текст): часто нужен ручной выбор или аккуратное merge по абзацам, если вы уверены в качестве. Для простых заметок безопаснее показать две версии и дать выбрать.
- Профиль (имя, фото, телефон): обычно LWW по каждому полю отдельно. Если поменяли фото и имя на разных устройствах, это можно слить без конфликта.
- Адрес доставки: чаще ручной выбор, потому что ошибка может стоить реальной доставки «не туда». Иногда допускают LWW, но только если вы явно показываете, что адрес был заменен.
- Статус заказа: обычно не редактируемый офлайн, или строго серверное правило. Если разрешить офлайн-изменения, то только с понятной очередью действий и подтверждением.
Как объяснять результат, чтобы не было сюрпризов
Правило должно звучать простыми словами и совпадать с тем, что реально произошло.
Если сработал LWW, помогает короткая и конкретная формулировка: «Мы сохранили вашу последнюю версию от 14:20». Не обещайте «историю», если ее нет.
Если изменения слиты, важно подчеркнуть, что ничего не пропало: «Мы объединили изменения с другого устройства. Проверьте итоговый текст».
Если нужен выбор, не пугайте словом «конфликт». Лучше так: «Есть две версии. Выберите, какую оставить: “Эта версия (с телефона)” или “Версия с планшета”». Добавьте один факт, который помогает решить: время, устройство, короткий превью.
Если вы делаете приложение в TakProsto, удобно заранее описать эти правила в планировании: по каждому объекту и полю указать LWW, merge или ручной выбор. Тогда интерфейс и тексты проще держать в одном стиле.
UX-копирайтинг: слова, которые снимают тревогу
В офлайн-режим пользователь приносит один главный страх: «я сейчас что-то сделаю без сети, а потом все пропадет или задвоится». Снять его можно не экранами настроек, а короткими повторяющимися фразами в правильных местах.
Статусы, которые не требуют расшифровки
Статус должен отвечать на один вопрос: где сейчас мои изменения. Лучше иметь 3-4 устойчивых состояния и не менять слова от экрана к экрану.
- Сохранено на устройстве (изменение уже в безопасности, даже без интернета)
- Ожидает отправки (в очереди, будет отправлено автоматически)
- Синхронизируется (идет обмен, можно продолжать работать)
- Синхронизировано (сервер и устройство совпадают)
- Нужна проверка (есть сомнение, просим участие)
Формулировки вроде «локальная запись» или «не закоммичено» понятны команде, но пугают пользователя. Выбирайте бытовые слова: «на устройстве», «отправка», «проверка».
Кнопки, тосты и ошибки: говорим, что делать дальше
Кнопки лучше называть действием, а не процессом: «Повторить отправку», «Сохранить здесь», «Посмотреть изменения». Короткие тосты должны подтверждать результат, а не внутреннюю кухню: «Сохранено. Отправим, когда появится сеть».
Сообщение об ошибке не должно заканчиваться на «не удалось». Дайте следующий шаг и успокойте про данные: «Не получилось отправить. Изменения сохранены на устройстве. Попробовать еще раз».
Когда заранее предупреждать о риске конфликта
Предупреждение нужно не всегда. Оно уместно, если пользователь редактирует то, что часто меняют другие (заказ, смена статуса, остатки). Тогда мягкая микрокопия снижает удивление: «Похоже, этот объект мог измениться на другом устройстве. При синхронизации попросим выбрать вариант».
На экране выбора держите тон нейтральным, без обвинений: «Мы нашли два варианта. Выберите, какой оставить». Если есть merge, поясните простыми словами: «Попробуем объединить. Проверьте итог перед сохранением».
Пошагово: как спроектировать синк, который работает
Синхронизация ломается не из-за «плохого интернета», а из-за неясных правил. Чтобы offline-first мобильные приложения вели себя предсказуемо, сначала договоритесь о правилах, а уже потом пишите код.
1) Зафиксируйте поведение как продукт
Начните не с протоколов, а с простой таблицы: функция - что можно офлайн - что требует сети - что увидит пользователь. Это быстро вскрывает спорные места (например, «можно ли удалять офлайн?» или «можно ли менять статус заказа без подтверждения сервера?»).
Дальше двигайтесь по шагам: опишите ключевые сценарии (создание, редактирование, удаление, просмотр, поиск) отдельно для офлайна и онлайна; для каждого объекта решите, что является «истиной» (устройство, сервер или последнее подтвержденное состояние); введите очередь изменений на устройстве, где каждое действие записывается как событие и может повторяться после сбоя.
2) Сделайте конфликты управляемыми
Конфликты неизбежны, если одно и то же меняют на двух устройствах. Важно заранее выбрать стратегию не «на весь объект», а там, где это действительно нужно: иногда достаточно LWW, иногда нужен merge, а иногда только ручной выбор.
Практичные решения, которые стоит зафиксировать заранее: стратегия конфликтов по полям и объектам; порядок синхронизации (и что делать при частичном успехе, когда 7 изменений прошли, а 1 нет); понятные статусы в интерфейсе и отдельный экран «Проблемы синка», где видно, что не отправилось, почему и что можно сделать (повторить, отменить, выбрать версию).
Пример: пользователь отредактировал адрес доставки на телефоне без сети, а на планшете в это время менеджер поменял адрес в карточке. Если для адреса стоит LWW, покажите в «Проблемах синка»: «Адрес обновлен на другом устройстве. Выберите: оставить новый или вернуть ваш». Если адрес хранится как набор полей, можно сделать merge и подсветить только спорные части.
Финальный шаг - тесты «как в жизни»: режим полета, переключение Wi‑Fi/4G, разрывы на середине отправки, два устройства с одним аккаунтом. Проверяйте не только «все прошло успешно», а что будет, когда все пошло не так, и понимает ли человек, что происходит.
Пример сценария: два устройства и редактирование без сети
Представим карточку клиента: имя, телефон, комментарий и статус сделки. Пользователь открыл карточку на телефоне и на планшете. Потом связь пропала.
На телефоне он меняет комментарий: «Перезвонить в пятницу». На планшете в это же время он меняет статус на «В работе» и правит телефон. Оба устройства уверены, что изменения сохранены, но они пока только локальные.
Чтобы не было тревоги, интерфейс прямо показывает, что именно произошло. Например, рядом с кнопкой «Сохранить» и в заголовке карточки:
- «Сохранено на устройстве» (и время последнего локального сохранения)
- «Нет сети. Отправим изменения автоматически»
- «В очереди на синхронизацию: 2 изменения»
Когда сеть появляется, синхронизация идет как понятная очередь: сначала отправляем локальные правки, потом подтягиваем обновления с сервера. Важный момент: пользователь не должен гадать, что уже «улетело». Хороший нейтральный текст в момент старта: «Связь восстановлена. Синхронизируем изменения…». После завершения: «Готово. Все изменения сохранены».
Теперь конфликт: сервер видит, что два устройства меняли одни и те же поля.
Вариант 1: last-write-wins (LWW)
Если вы выбрали LWW, правило должно быть простым и озвученным. Например: «Если одно и то же поле изменено на разных устройствах, сохраняется самое позднее изменение по времени синхронизации». Тогда в нашем примере комментарий с телефона сохранится, а по телефону и статусу победит тот вариант, который пришел последним.
Пользователю важно показать итог без обвинений: «Мы объединили изменения автоматически. Проверьте поля, отмеченные как обновленные».
Вариант 2: ручной выбор (когда цена ошибки высока)
Если потеря данных недопустима (контакты, суммы, адреса), лучше остановиться и спросить. Диалог должен быть коротким и по делу:
- «На телефоне: +7 999… / На планшете: +7 911…»
- «Что оставить?»
- Кнопки: «Оставить с телефона», «Оставить с планшета», «Сохранить оба (в комментарий)»
Финальная проверка после синка тоже должна быть простой: «Открыть карточку». В карточке пользователь видит отметку «Синхронизировано только что», а спорные поля можно подсветить на пару секунд. Это дает ощущение контроля и снижает страх, что что-то потерялось.
Частые ошибки и ловушки в офлайн-синхронизации
Самая частая причина жалоб на offline-first мобильные приложения - не баги синка, а ощущение, что приложение «обмануло». Пользователь сделал действие, увидел «Сохранено», а потом данные пропали или «переехали» назад после появления сети.
Ошибки, которые ломают доверие
Первая ловушка - прятать офлайн-состояние. Если сеть пропала, это должно быть видно: небольшая плашка, иконка, строка статуса. Когда приложение делает вид, что все как обычно, любой откат после синхронизации выглядит как потеря данных.
Вторая ловушка - писать «Сохранено» без уточнения, где именно. Пользователь понимает это как «на сервере и у всех». Если сохранение локальное, так и говорите: «Сохранено на устройстве. Отправим, когда появится интернет».
Третья ловушка - смешивать черновики и опубликованные данные. Например, карточка заказа выглядит «официальной», хотя это локальный черновик, который еще не принят сервером. Нужен явный статус: «Черновик», «Отправляется», «Синхронизировано», «Нужна проверка».
Четвертая ловушка - полагаться на время устройства для LWW. Часы на телефоне могут отставать, быть переведены вручную или сбиться после смены часового пояса. Для конфликтов лучше опираться на серверные версии, монотонные счетчики или историю изменений.
Пятая ловушка - не продумать удаление. Удаление должно синхронизироваться так же строго, как создание: с «надгробием» (tombstone) и понятным окном восстановления. Иначе удаленный элемент внезапно «воскреснет» при синке со второго устройства.
Еще одна частая проблема - разные правила на разных экранах. Если в списке правки сливаются, а в профиле действует LWW, пользователь будет считать это случайностью.
Чтобы поймать ошибки рано, полезно пройти один сценарий руками: создать запись офлайн, отредактировать на втором устройстве, удалить на первом, потом включить сеть. Если вы собираете прототип в TakProsto, заложите статусы и тексты сразу, а не «после того как синк заработает».
Быстрый чеклист перед релизом офлайн-режима
Перед релизом офлайн-режима важно не только «чтобы синхронизировалось», но и чтобы человек понимал, что происходит. Для offline-first мобильных приложений это часть доверия: пользователь должен видеть статус и знать, что делать, если что-то пошло не так.
Проверьте интерфейс и тексты: офлайн не должен выглядеть как поломка.
- Есть заметный индикатор статуса: «Офлайн», «Подключаемся», «Синхронизация…», «Синхронизировано».
- Ясно разделено «сохранено на устройстве» и «уже в облаке».
- Есть место, где видно очередь: раздел или экран «Ожидает синхронизации» с количеством элементов.
- Для ключевых полей заранее выбраны правила конфликтов: где работает LWW, а где нужно merge или ручной выбор.
- При ошибке есть понятные действия: «Повторить», «Отменить изменения», «Выбрать версию».
Дальше пройдитесь по жизненным сценариям. Минимальный набор тестов лучше делать не в идеальной сети, а в реальности: в лифте, в метро, в зоне с 1-2 делениями.
Сценарии, которые чаще всего ломают синк
Проверьте полное отсутствие сети, «плохую» сеть (обрывы, задержки), смену аккаунта на устройстве, вход с другого устройства и параллельное редактирование одного и того же объекта.
Простой пример: пользователь изменил адрес доставки на телефоне без сети, а на планшете онлайн поменял номер телефона в том же профиле. Если у вас LWW, пользователь должен увидеть, что именно перезаписалось и почему. Если merge, должно быть понятно, какие поля объединились автоматически.
План действий при проблеме
Если синк не удался, человек должен получить короткое объяснение без обвинений: что не так (нет сети, конфликт, ошибка сервера) и что будет дальше (попробуем снова, нужно выбрать версию, изменения останутся на устройстве). В TakProsto это удобно проверять на прототипе через сценарии и статусы еще до того, как вы «дополируете» логику на проде.
Следующие шаги: как быстро проверить идею и не утонуть в деталях
Начните не с алгоритмов, а с карты рисков. Выпишите 10-20 объектов, которые люди реально меняют в дороге, в подвале, в самолете: заметки, заказы, комментарии, черновики, статусы. Для каждого объекта отметьте поля, где конфликт наиболее вероятен (например, сумма, статус, дедлайн, список товаров). Эта таблица быстро покажет, где хватит LWW, а где нужно merge или ручной выбор.
Дальше соберите минимальный «безопасный» офлайн-набор для первой версии. Он должен быть маленьким и честным: то, что вы гарантируете без сети. Например: просмотр кеша, создание черновиков, редактирование своих заметок, отправка очереди при появлении сети. Остальное лучше временно сделать «только онлайн», чем получить скрытые потери данных.
Параллельно сделайте прототип не логики, а экранов статусов и выбора версий. Люди судят офлайн-режим по ощущению контроля. Проверьте на 5-10 пользователях (хватит даже внутри команды) два вопроса: понимают ли они, что данные сохранены локально, и что будет при конфликте. Если человек не может пересказать правило своими словами, правило слишком сложное.
План работ, который часто укладывается в несколько дней:
- Список объектов и конфликтных полей, плюс выбранный паттерн (LWW, merge, ручной выбор) для каждого.
- Прототип трех состояний: «Офлайн», «Есть несинхронизированные изменения», «Найдено расхождение».
- Черновая UX-копия для этих состояний (коротко, без технических слов).
- Минимальная реализация синка в прототипе: мобильное приложение на Flutter, сервер на Go и база PostgreSQL.
- Тестовый сценарий «два устройства»: одно правит офлайн, второе правит онлайн, затем первое выходит в сеть.
Если вам важно быстро накидать и проверить правила синхронизации, уместно использовать TakProsto (takprosto.ai): в чате описать объекты, правила и тексты сообщений, включить planning mode, а затем фиксировать изменения через снимки, чтобы легко откатиться, если выбранный паттерн не подходит. Когда прототип подтвердит правила, можно выгрузить исходники и довести до продакшена уже без сюрпризов.
Мини-подсказка по формулировке цели: «После недели использования офлайн люди не спрашивают, куда делись изменения». Это измеримо: по вопросам в поддержку и по короткому тесту, где пользователь сам объясняет, что произойдет при расхождении версий.
FAQ
Почему офлайн-режим ломает доверие, если не объяснить правила?
Лучше считать, что есть минимум три разных состояния:
- Сохранено на устройстве (изменение не пропадет при закрытии приложения).
- Отправлено на сервер (запрос ушел, но еще не подтвержден).
- Подтверждено сервером / синхронизировано (сервер принял, и это «общая правда»).
Если в интерфейсе показывать одно слово «Сохранено» для всех трех, пользователь не понимает, насколько надежно это действие.
Какие статусы офлайна лучше всего показывать пользователю?
Используйте простой словарь и не меняйте его от экрана к экрану. Практичный набор:
- «Сохранено на устройстве»
- «Ожидает отправки»
- «Синхронизируется»
- «Синхронизировано»
- «Нужна проверка» (когда есть расхождение или спорное место)
Эти статусы отвечают на главный вопрос: где сейчас мои изменения и что будет дальше.
Какие фразы в интерфейсе безопасны, а какие лучше не обещать?
Безопасные формулировки — те, которые точно соответствуют фактам:
- «Сохранено на устройстве. Отправим, когда появится сеть»
- «Последняя синхронизация: 12:40» (именно время подтверждения сервером)
- «Есть несинхронизированные изменения: 3»
Опасные формулировки — те, которые звучат как гарантия «везде и навсегда», хотя это не так:
- «Все данные сохранены» (если только локально)
- «Синхронизировано» (если нет подтверждения сервера)
- «Ничего не потеряется» (если возможны конфликты и перезаписи)
Что вообще стоит разрешать делать офлайн, а что лучше запретить до онлайна?
Дайте честные границы. Обычно офлайн можно делать:
- просмотр уже загруженных данных;
- создание новых записей (как черновики);
- редактирование того, что уже было открыто.
Часто лучше оставить «только онлайн»:
- загрузку тяжелых файлов (фото/видео/документы);
- глобальный поиск по всем данным;
- операции, где нужна строгая серверная проверка (например, финальное подтверждение заказа).
Так проще гарантировать результат и объяснить поведение.
Как правильно выстроить очередь синхронизации, когда сеть вернулась?
Начните с того, что дает чувство завершенности:
- новые записи и важные изменения статусов;
- короткие правки полей и комментарии;
- только потом — тяжелые вложения, превью и «украшательства».
Дополнительно помогает правило: крупные файлы отправлять по Wi‑Fi или по явному действию пользователя, чтобы не «съесть» мобильный интернет и не заблокировать очередь.
Зачем определять «единицу данных» для синхронизации и как выбрать ее?
«Единица синка» — это то, что пользователь воспринимает как целое: заказ, заметка, задача, карточка клиента.
Хорошее правило: единица должна иметь понятную границу «сделано / не сделано». Тогда вы можете честно говорить: «Заказ сохранен на устройстве и будет отправлен целиком».
Если синхронизировать «кусочками» (поле ушло, поле не ушло), это выглядит как баг и порождает вопросы: почему статус обновился, а адрес нет.
Как избежать дублей и странных откатов при синхронизации?
Минимальный набор, который резко снижает дубли и «воскрешение» данных:
- local_id, который создается сразу и не меняется;
- отдельный server_id, который появится после синка;
- version (счетчик изменений) и поля типа updated_at / updated_by.
Не полагайтесь только на время устройства: часы могут быть неверными. Версия или серверный порядок операций обычно надежнее.
Почему лучше хранить журнал изменений, а не отправлять «текущее состояние целиком»?
Очередь операций проще отлаживать, чем «слепок текущего состояния». Записывайте события вида:
- создать / изменить / удалить;
- какой объект;
- какие поля поменялись;
- какая версия.
Если синхронизация оборвалась, вы просто повторяете неотправленные операции. Пользователю это можно объяснять так: «Изменения сохранены на устройстве и будут отправлены позже».
Как выбрать между LWW, merge и ручным выбором при конфликтах?
Выберите правило под тип данных (а не одно правило на всё):
- LWW (последняя запись победила) — для низкой цены ошибки и независимых полей (например, имя или фото профиля). Минус: можно тихо потерять чужую правку.
- Merge (слияние) — для списков и независимых полей, где можно объединить без смысла «кто прав». Плохо подходит для связного текста.
- Ручной выбор — для дорогих ошибок (адрес доставки, суммы, важные реквизиты).
Главное — чтобы результат можно было предсказать и объяснить одной фразой в интерфейсе.
Что показывать пользователю, если синк не прошел или нашлись две версии данных?
Сделайте отдельное место, где видно, что не так, и дайте 1–2 простых действия:
- «Не получилось отправить. Изменения сохранены на устройстве»
- кнопки: «Повторить отправку», «Посмотреть изменения», «Отменить»
Для расхождений не обязательно слово «конфликт». Нейтральнее:
- «Есть две версии. Выберите, какую оставить»
- показывайте подсказку: время, устройство, короткий фрагмент.
В TakProsto удобно заранее описывать эти статусы и действия в planning mode, чтобы тексты и логика не расходились.