Машина состояний онбординга: остановите полузавершённые регистрации
Узнайте, как машина состояний онбординга предотвращает полузавершённые аккаунты, обрабатывает обновления и задержки почты и помогает пользователям продвигаться дальше.

Почему пользователи застревают во время онбординга
«Полузавершённые аккаунты» появляются, когда поток регистрации создаёт запись пользователя, но человек никогда не достигает рабочего завершённого состояния. Они могут видеть вечный спиннер, попадать на экран «подтвердите почту», который не обновляется, или каждый раз при входе возвращаться к шагу номер один.
Большинство потоков ломаются потому, что предполагают онбординг как одноразовый линейный чеклист. Реальные пользователи обновляют страницу, когда что‑то медленно, нажимают назад, открывают ту же ссылку на телефоне после начала на ноутбуке или возвращаются завтра после истечения сессии.
Обычные триггеры выглядят просто, но стоят дорого:
- Сетевая ошибка ломает шаг после создания аккаунта.
- Письмо для верификации приходит с задержкой или пользователь кликает старую ссылку в письме.
- Таймаут разлогинивает пользователя посреди шага, и приложение теряет след прогресса.
- Несколько вкладок отправляют один и тот же шаг дважды и портят данные.
- Обновление страницы повторяет действие, которое нельзя повторять (например, снятие денег или создание команды).
Последствия быстро проявляются в саппорте: «Не могу войти», «скажут, что подтверждён, но всё ещё заблокирован», или «постоянно просят снова задать пароль». Но большая потеря — молчаливая: многие просто уходят и не обращаются в поддержку.
Основное обещание машины состояний онбординга просто: что бы ни случилось, пользователь всегда может безопасно возобновить процесс. Если шаг не удался, приложение точно знает, где он остановился, что можно сделать дальше и что ещё недоступно.
Машины состояний простыми словами
Состояние — это просто чёткая метка того, где пользователь находится в процессе регистрации. Не чувство и не название страницы. Думайте: account created, email not verified, profile incomplete, payment pending, onboarded.
Линейный чеклист предполагает, что люди идут вперёд один раз и по порядку. В реальности кто‑то обновляет страницу во время оплаты, открывает письмо для подтверждения через час, нажимает назад или пробует снова с другой вкладки. С чеклистом система часто не понимает, продолжать ли процесс, перезапускать его или блокировать пользователя.
Машина состояний онбординга — это небольшой набор правил, который отвечает на два вопроса каждый раз:
- В каком состоянии находится этот пользователь?
- Из этого состояния какие переходы разрешены (вперёд, повтор, назад)?
Вместо угадываний по «текущему экрану» вы храните состояние в записи пользователя и обновляете его только когда шаг действительно прошёл успешно. Если шаг провалился, состояние не меняется. Если пользователь пробует снова, вы безопасно повторяете тот же шаг. Если он возвращается позже, вы смотрите на сохранённое состояние и отправляете его к следующему валидному шагу.
Держите модель простой. Начните с тех состояний, которые реально меняют то, что пользователь должен увидеть дальше. Не создавайте отдельного состояния для каждой кнопки.
Пример: пользователь создал аккаунт и ждёт подтверждения почты. Он обновляет страницу, а потом пытается войти с другого устройства. Если его состояние — email_unverified, маршрутизация всегда может отправить его на «повторно отправить подтверждение», но ни в коем случае не на «завершить профиль», пока верификация не будет пройдена. Одна такая простая правило спасает много застрявших регистраций.
Выбирайте состояния, которые важны
Машина состояний работает лучше, когда вы записываете моменты, которые меняют то, что пользователь может делать дальше. Если записывать каждый клик, вы создадите шум и крайние случаи. Если записывать слишком мало — восстановление будет ненадёжным.
Практическое правило: создавайте состояние, когда вы (1) записали что‑то важное в базу данных, (2) запустили что‑то внешнее (например, отправили письмо), или (3) следующий экран должен отличаться, если пользователь обновит страницу.
Большинство потоков регистрации покрываются небольшим набором состояний, например:
started(аккаунт создан)email_sent(запрошено письмо подтверждения)verified(почта подтверждена)profile_done(обязательные поля профиля заполнены)completed(онбординг завершён)
Не все состояния должны блокировать пользователя. Решите, какие обязательны для перехода дальше, а какие — опциональны. Подтверждение почты может быть обязательным для безопасности, а добавление фото профиля — опциональным.
Дальше решите, что приложение должно показывать для каждого состояния. Именно здесь многие застрявшие аккаунты спасают.
Если пользователь попадает с state = email_sent, не показывайте общий баг‑экран и не перезапускайте регистрацию. Покажите понятный экран «Проверьте почту» с кнопкой повторной отправки, опцией смены адреса и краткой подсказкой о возможных задержках.
Если state = verified, но профиль неполный, отправляйте сразу на форму профиля и подгружайте уже сохранённые данные.
Определите допустимые переходы
Машина состояний помогает только если вы строго определяете, что может происходить дальше. Напишите простую карту: для каждого состояния перечислите несколько состояний, в которые можно перейти. Держите это предсказуемым.
Думайте в терминах ходов, а не экранов. Экран может меняться без изменения состояния. Состояние должно меняться только когда у вас есть доказательство успеха шага.
Правила, которые хорошо работают для большинства потоков:
- Для каждого состояния давайте 1–3 допустимых следующих состояния.
- Добавляйте безопасные действия, которые не меняют состояние, например «снова открыть приложение» или «обновить страницу».
- Раннее определяйте терминальные состояния, например
completed(готово) иdisabled(заблокирован из‑за мошенничества, чарджбэков или нарушений политики). - Обрабатывайте «ожидание» как реальное состояние, например
email_verification_pending, а не временный экран. - Решите, что делать при недействительном переходе (пропустить шаг, отправка из старой вкладки, двойной клик). Обычно: игнорировать запрос, показать понятное сообщение и направить пользователя на правильный шаг.
Пример: пользователь регистрируется, попадает в email_verification_pending, потом закрывает браузер. Два дня спустя он кликает ссылку подтверждения. Ваши правила должны позволять email_verification_pending -> email_verified, а затем переводить его на следующий шаг. Нельзя позволять email_verification_pending -> completed просто потому, что какой‑то «финишный» endpoint был вызван.
Пошагово: реализуем возобновляемый онбординг
Возобновляемый поток даёт одно обещание: где бы пользователь ни прервался (обновление, сбой оплаты, медленное письмо), приложение всегда может решить, что показать дальше.
Начните с хранения единого источника правды для прогресса. Добавьте поле onboarding_state в запись пользователя или создайте отдельную запись онбординга, если вам нужна история, ретраи или несколько попыток.
Далее централизуйте логику маршрутизации. Напишите одну функцию, которая принимает пользователя (и при необходимости запись онбординга) и возвращает следующий экран, например "verify_email" или "create_workspace". Эта функция — сердце вашей машины состояний онбординга.
Потом относитесь к каждому шагу как к «завершённому только при успехе». Если пользователь отправил форму профиля, сначала провалидируйте и сохраните, и только потом переходите состояние вперёд. Если что‑то пошло не так, держите состояние там же, чтобы пользователь мог повторить.
Короткий чеклист реализации:
- Сохраняйте состояние в базе данных (а не только в браузере)
- Используйте одну функцию «решить следующий шаг» везде
- Продвигайте состояние только после реального успеха шага
- При каждом загрузке приложения маршрутизируйте по текущему состоянию
- Ведите небольшой аудит‑лог изменений состояния
Этот аудит‑лог важнее, чем кажется. Когда саппорт видит «Я подтвердил почту, но всё ещё заблокирован», лог должен показать, получена ли верификация и в каком состоянии сейчас аккаунт.
Пример: Сэм регистрируется на мобильном, запрашивает верификацию, затем открывает приложение на десктопе позже. Если приложение всегда вызывает ту же функцию принятия решения при загрузке, Сэм попадёт на «Подтверждена? Да. Дальше: создать рабочее пространство», вместо мёртвой страницы или петли.
Обработка обновлений, кнопки «Назад» и нескольких вкладок
Рассматривайте онбординг как то, что можно прервать в любой момент. Люди обновляют страницу, когда что‑то кажется застрявшим, нажимают назад и открывают две вкладки, если код не появляется. Если ваш поток работает только в идеальной одновкладочной последовательности, вы создадите застрявшие аккаунты.
Безопасное правило: любая страница может перезагрузиться в любой момент. При каждой загрузке спрашивайте у сервера, в каком состоянии пользователь в машине состояний онбординга, а затем рендерьте шаг, соответствующий этому состоянию. Не полагайтесь на последний URL как на истину. URL — это навигация; состояние — источник правды.
Избегайте хранения критического прогресса только в браузере (localStorage, переменные в памяти, React‑стейт). Это легко теряется при обновлении, в приватном режиме или при смене устройства. Храните прогресс на бэкенде с временными метками, а клиентское хранение используйте лишь как удобство.
Несколько вкладок создают проблему «двойной отправки». Если две вкладки пытаются завершить один и тот же шаг, вторая не должна ломать аккаунт. Отвечайте «уже выполнено» и направляйте дальше.
Некоторые UI‑решения уменьшают панику:
- Сделайте понятную кнопку «Продолжить онбординг» в приложении после входа.
- Показывайте текущий шаг и что будет дальше.
- Если шаг ожидает, объясняйте почему и что решит ситуацию.
- Предлагайте «Перезапустить онбординг» только если это безопасно и без потери данных.
Задержки верификации почты без тупиков
Верификация почты по природе медленная. Письма задерживаются, попадают в спам или приходят после того, как пользователь закрыл вкладку. Если ваш поток предполагает, что подтверждение мгновенное, у вас будут регистрации, которые никогда не восстановятся.
В машине состояний относитесь к верификации как к асинхронной работе. Хороший паттерн — разделять «мы отправили письмо» и «адрес подтверждён». Держите состояние вроде email_sent (или awaiting_email_verification) и отдельный флаг email_verified = true/false. Так пользователь может обновить страницу, вернуться завтра или переключиться на другое устройство без попадания в тупик.
На экране ожидания давайте безопасные действия, которые не сбрасывают прогресс и не создают дубликаты:
- Повторно отправить письмо с подтверждением (с ограничением частоты, для того же пользователя и в том же состоянии)
- Проверить статус подтверждения
- Сменить адрес почты (с подтверждением и отправкой нового письма)
- Использовать другой способ входа (только если вы это поддерживаете и только после подтверждения личности)
Также обрабатывайте случай «неправильный адрес» простым пояснением. Одна строка типа «Использовали неправильный адрес? Обновите его здесь, и мы вышлем новую ссылку.» предотвращает много тикетов.
Сценарий: Сэм регистрируется на мобильном, потом идёт на ноутбук, чтобы закончить настройку. Письмо подтверждения приходит с опозданием, и Сэм кликает его на телефоне. Если ваше приложение хранит ожидающее состояние и проверяет email_verified при следующей загрузке, Сэм сможет продолжить на ноутбуке сразу. Никаких новых аккаунтов и петлей возврата к шагу один.
Делайте шаги повторяемыми и идемпотентными
Люди нажимают кнопку дважды. Телефоны теряют соединение. Браузеры ретраят запросы. Если ваш онбординг предполагает, что каждый шаг выполняется ровно один раз, у вас будут аккаунты в промежуточном состоянии.
Повторяемый шаг — это когда его можно попытаться выполнить снова после ошибки. Идемпотентный — когда выполнение дважды даёт тот же результат, что и один раз. Вам нужны оба свойства.
Простой пример — «Создать рабочее пространство». Если пользователь нажал дважды или таймаут вернул ошибку и приложение повторило запрос, вы не должны получить два пространства, два биллинга или невозможность продолжить потому, что второй запрос упал.
Практический паттерн — ключ идемпотентности. Когда клиент начинает действие (например, создание рабочего пространства), генерируйте уникальный ключ для этого действия и отправляйте его вместе с запросом. На сервере сохраняйте ключ с полученной записью. Если тот же ключ приходит снова, возвращайте уже созданный результат вместо создания дубликата.
Правила, которые предотвращают большинство застрявших аккаунтов:
- Обрабатывайте каждое действие «create» как «создать или вернуть существующее», связанное с idempotency token.
- Делайте уникальные ограничения в базе данных (например, одно рабочее пространство на пользователя во время онбординга).
- Переходите к следующему состоянию только после консистентности в базе.
- Если что‑то падает посреди шага, записывайте понятную причину сбоя и держите пользователя в предыдущем безопасном состоянии.
- Делайте UI безопасным (например, дизейбл кнопки во время запроса), но не полагайтесь только на это.
Распространённая ошибка — сначала обновить состояние онбординга, а затем попытаться создать данные. Если вторая часть падает, пользователь выглядит «пройденным» через шаг, но данных нет.
Типичные ошибки, создающие застрявшие аккаунты
Большинство багов онбординга банальны. Они возникают, когда приложение отмечает прогресс слишком рано или доверяет браузеру больше, чем серверу.
Обычная ловушка — пометить шаг как завершённый до того, как он реально завершён. Например, установить payment_complete, когда загрузилась страница чек‑аута, а не когда платёж подтвердился. Если вкладка обновится или callback потеряется, пользователь окажется в состоянии, которое он на самом деле не может удовлетворить.
Ещё одна частая причина — опираться на состояние на клиенте (localStorage, флаги в памяти, индекс шага в React) для решения, что показывать дальше. Пользователи открывают новую вкладку, очищают хранилище или сменяют устройство, и ваш поток теряет последовательность. В машине состояний сервер — источник правды, а UI — его отображение.
Ошибки, которые стабильно создают полузавершённые аккаунты:
- Прогресс закодирован в URL (например
/onboarding/step-3) вместо статуса на сервере, который бэкенд принуждает. - Создание новой записи пользователя при каждой неудачной попытке верификации, что приводит к дубликатам и путанице «не тот аккаунт».
- Слабая обработка задержек верификации почты: приложение ожидает мгновенного клика и блокирует все остальные действия.
- Нет безопасного пути повтора: повторная отправка создаёт второе рабочее пространство, вторую подписку или битый профиль.
- Нет выхода: когда что‑то ломается, у пользователя нет понятного способа запросить помощь или сброс.
Пример: Сэм регистрируется, запрашивает письмо для подтверждения, затем обновляет страницу в ожидании. Фронтенд продвигает счётчик шагов и предполагает, что Сэм подтверждён, но бэкенд всё ещё требует верификации. В результате Сэм попадает в петлю перенаправлений.
Быстрая проверка перед релизом
Перед релизом тестируйте поток так, как это делают реальные люди: обновление страницы, переключение устройств, кнопка Назад и ожидание часов для письма. Если ваша машина состояний справляется с этим без путаницы, вы избежите большинства застрявших аккаунтов.
Для каждого состояния проверьте:
- Одно состояние отображает один понятный экран (или сообщение) и одно ясное следующее действие.
- Обновление страницы сохраняет прогресс и показывает тот же «что дальше».
- Повторная отправка одного и того же шага дважды не создаёт дубликатов (аккаунтов, организаций, платежей, приглашений).
- Верификацию можно повторно отправить, перепроверить и завершить позже без перезапуска регистрации.
- Саппорт видит текущее состояние и недавние переходы (временные метки и ошибки).
Затем выполните три теста «сломай намеренно» в реальном браузере:
-
Откройте один и тот же шаг в двух вкладках, завершите в Вкладке A, затем перезагрузите Вкладку B. Вкладка B должна безопасно догнать изменения.
-
Отключите интернет посреди шага, отправьте, затем попробуйте снова при подключении. Вы либо получите тот же результат успеха, либо понятную ошибку с безопасным повтором.
-
Задержите верификацию: запросите письмо, подождите 10 минут, кликните ссылку и вернитесь в приложение. Вы должны попасть в нужное место, а не на пустую страницу или в петлю «начать сначала».
Пример: реалистичное спасение застрявшего онбординга
Майя регистрируется на пробный период SaaS в загруженное время дня. Она создаёт аккаунт, видит «Проверьте почту для подтверждения», затем закрывает вкладку.
На следующий день письмо для подтверждения наконец приходит. Она кликает, но её оригинальная сессия уже истекла. В хрупком потоке это мёртвая точка: приложение не понимает, новый ли она пользователь, подтверждена ли она или полу‑создана.
С машиной состояний онбординга приложение смотрит на текущее состояние Майи и возобновляет процесс. Клик по ссылке помечает аккаунт как подтверждённый, даже если она вышла из системы, а затем переводит её к следующему требуемому шагу.
Несколько минут спустя Майя открывает приложение в двух вкладках. В одной вкладке она заполняет профиль; в другой всё ещё видит «Завершите профиль». Когда она нажимает Сохранить в первой вкладке, состояние аккаунта продвигается один раз. Вторая вкладка при обновлении видит новое состояние и не перезаписывает ничего.
Опыт Майи остаётся последовательным:
- Регистрируется: видит «Подтвердите почту» с безопасной опцией «Повторно отправить».
- Кликает позднюю ссылку: верификация проходит и её просят войти.
- Входит: попадает на «Завершите профиль», а не на главную страницу.
- Использует две вкладки: отстающая вкладка догоняет с «Уже завершено» и идёт дальше.
Саппорт тоже видит понятную картину: текущее состояние, временная метка последнего завершённого шага и последняя ошибка (если была). Это разница между решением проблемы за минуты и угадыванием часами.
Следующие шаги: спроектируйте поток и быстро исправьте застревания
Если люди могут зарегистрироваться, но не могут надёжно завершить процесс, рассматривайте онбординг как продуктную функцию, а не набор экранов. Простая машина состояний даёт единый источник правды о том, где пользователь и что ему разрешено делать дальше.
Начните на бумаге. Перечислите каждый шаг, который ожидает ваше приложение (создать аккаунт, подтвердить почту, принять условия, создать рабочее пространство, добавить оплату, пригласить команду). Затем сгруппируйте их в 5–8 чётких состояний, которые описывают прогресс, а не страницы. Состояния должны оставаться стабильными, даже если UI поменяется.
Быстрый план, который обычно окупается:
- Определите состояния и одно конечное состояние (например: SignedUp, EmailPending, Verified, ProfileComplete, Active).
- Логируйте каждое изменение состояния (кто, откуда, куда, когда и почему).
- Отслеживайте отказы по состояниям, а не по страницам.
- Добавьте одну центральную «маршрут по состоянию» логику, чтобы обновления и глубокие ссылки попадали правильно.
- Выберите один хрупкий шаг (часто это задержки верификации почты или оплата) и сначала сделайте его возобновляемым.
Небольшая привычка, которая предотвращает много боли: когда что‑то падает, не оставляйте пользователя в «неизвестности». Верните его в известное состояние с понятным следующим действием, даже если это «повторить» или «проверьте почту позже».
Если вы унаследовали приложение, сгенерированное ИИ, где регистрация работает в демонстрации, но ломается при реальных повторах, задержках писем и переключении вкладок, FixMyMess (fixmymess.ai) фокусируется на диагностике и ремонте этих потоков: сохранение состояния, проверки переходов и бэкенд‑логика, которая не даёт пользователям застревать в процессе онбординга.
Часто задаваемые вопросы
Что такое машина состояний для онбординга и зачем она нужна?
Машина состояний делает онбординг возобновляемым. Вместо того чтобы догадываться о прогрессе по текущей странице, вы храните ясное поле onboarding_state у пользователя и двигаете его только когда шаг действительно завершён. Это предотвращает застревания при обновлениях, ретраях и смене устройств.
Сколько состояний онбординга мне стоит использовать?
Начните с тех немногих состояний, которые меняют то, что пользователь может делать дальше. Большинство продуктов покрывают регистрацию 5–8 состояниями, например account created, email unverified, profile incomplete, completed. Создавать состояние для каждого клика — плохая идея: это добавляет крайних случаев без пользы для восстановления.
Где хранить прогресс онбординга — на фронтенде или бэкенде?
Сохраняйте прогресс на бэкенде, обычно в поле onboarding_state в записи пользователя. Хранение в браузере можно использовать как удобство, но оно теряется при обновлении, режиме инкогнито, смене устройства или мульти-вкладках.
Могут ли пользователи безопасно возобновить онбординг после закрытия вкладки или смены устройства?
Да — если вы централизуете маршрутизацию. При каждом входе в приложение вызывайте одну функцию «decide next step», которая читает сохранённое состояние и отправляет пользователя на нужный экран, даже если он откроет приложение через день или с другого устройства.
Как предотвратить тупики из‑за задержек с верификацией почты?
Обрабатывайте верификацию почты как асинхронную задачу. Держите явное ожидающее состояние, например email_sent, и отдельный флаг email_verified. Показывайте экран «Проверьте почту», который можно обновлять, с возможностью повторной отправки и смены адреса, не перезапуская регистрацию.
Как избежать дублирующих записей при двойной отправке из нескольких вкладок?
Сделайте критические действия идемпотентными. Используйте idempotency key для операций создания (рабочего пространства, биллинга и т.п.) и уникальные ограничения в базе, чтобы повторная отправка возвращала уже созданный результат, а не создавала дубликат или ошибку.
Что должно происходить, если кто‑то пытается пропустить шаги или отправляет старую форму?
Не продвигайте состояние вперёд, пока не получите подтверждение успешного выполнения шага. Отклоняйте или игнорируйте недопустимые переходы. Если пользователь пытается пропустить шаг, покажите понятное сообщение и направьте его к корректному следующему шагу на основе сохранённого состояния.
Нужен ли мне аудит‑лог онбординга?
Ведите небольшой аудит‑лог изменений состояния с временными метками и причинами ошибок. Это превращает «Я верифицировал почту, но всё ещё заблокирован» из гадания в быстрый просмотр, какие события произошли и в каком состоянии сейчас аккаунт.
Какие самые быстрые проверки провести перед релизом?
Тестируйте так, как ведут себя реальные люди: обновляют страницу, открывают две вкладки, переключают устройство и ждут письма часами. Цель — каждое обновление должно направлять к валидному следующему действию, а повторы не должны портить данные.
Как починить поток регистрации, сгенерированный ИИ, который застревает пользователей?
Ищите прогресс, завязанный на фронтенде (счётчики шагов, прогресс в URL) и обновления состояния до подтверждения записи в базе или внешнего callback'а. Быстро сгенерированный код часто работает в демо, но ломается под ретраями и таймаутами; решение — перенести логику принятия решений и хранения состояния на сервер и сделать шаги идемпотентными.