Реальний час у застосунках, створених ШІ: як побудувати спільну роботу, не ламаючи чужі правки

Спільна робота в реальному часі ламається, коли двоє людей редагують застосунок одночасно — правки одного безслідно зникають, перезаписуються або суперечать тому, що бачить інший. Три типові збої і три виправлення, впроваджені по черзі, вирішують цю проблему.

Що відбувається, коли двоє людей редагують один застосунок одночасно?

Спільна робота в реальному часі — це те, що не дає двом людям перезаписати правки одне одного, коли вони одночасно редагують дані одного застосунку. Пропустіть цей крок — і збереження другого користувача може безслідно стерти те, що зробив перший. Ось як це виглядало для однієї команди.

Користувач створив спільний список завдань для своєї команди. У п’ятницю вдень двоє колег відкрили його одночасно. Обидва бачили:

  • Завдання 1: Продукти
  • Завдання 2: Подзвонити мамі
  • Завдання 3: Запланувати зустріч

Колега А відмітив “Продукти” як виконане. Колега Б додав “Полагодити роутер”. Обидва натиснули “зберегти”.

Коли колега А оновив сторінку, він побачив:

  • Завдання 1: Продукти (відмічено)
  • Завдання 2: Подзвонити мамі
  • Завдання 3: Запланувати зустріч

“Полагодити роутер” зникло. Робота колеги Б випарувалась.

Це — колізія: одночасні записи, і правки одного з них зникають. Звучить як окрема функція, а насправді це виправлення втрати даних. Без нього ваш застосунок ламається в ту ж мить, коли до нього торкнуться двоє людей одночасно.

Які найпоширеніші баги спільної роботи в реальному часі?

Спільна робота в реальному часі ламається трьома типовими способами: запис безслідно губиться, на екрані показуються застарілі дані, або двоє людей опиняються перед суперечливими фактами. Кожен випадок проявляється по-своєму, і кожен вимагає окремого виправлення.

Збій 1: Втрачений запис (тиха втрата даних)

Двоє людей зберігають зміни одночасно. Друге збереження перезаписує перше. Другий користувач бачить, що його зміна застосувалась, перший бачить… нічого. Або оновлює сторінку і дивується, куди поділась його робота.

Реальна історія: організаторка весілля та її асистентка працюють над списком гостей. Асистентка додає три підтвердження участі, поки організаторка позначає два як “фінальні”. Позначки організаторки зникають. Ніхто цього не помічає, поки організаторка не рахує двічі під час контрольних дзвінків — і запрошує людей, які вже й так підтвердили присутність.

Більшість реальних застосунків вирішують це, зберігаючи кожне натискання клавіші, а не лише клік на “Зберегти”. Google Sheets, Notion, Figma — усі так роблять. Вашому застосунку потрібна така ж поведінка.

Збій 2: Застаріле оновлення (бачити старі дані)

Користувач А редагує завдання. У користувача Б відкрита сторінка; він бачить стару версію. Він вносить зміну, спираючись на застарілі дані. Тепер виник конфлікт, якого він не бачить.

Реальна історія: страховий агент і підрядник працюють над страховою заявкою. Агент змінює “орієнтовна вартість ремонту: $3K” на “$5K” на основі нових фото. У підрядника на сторінці все ще $3K. Він подає форму на затвердження суми в $3K. Пізніше вони виявляють розбіжність.

Без оновлень у реальному часі обидва користувачі думають, що працюють з однією й тією ж версією. Насправді ні.

Збій 3: Каскадна суперечність (дві правди)

Користувач видаляє запис. Інший користувач у цей момент дивиться на деталі цього ж запису. Один бачить “видалено”, інший все ще бачить повний запис. Тепер вони діють, спираючись на різні факти.

Реальна історія: координаторка волонтерів позначає зміну як “скасовану”. Волонтер ще не оновив сторінку; для нього зміна досі “відкрита”. Він починає набирати людей на неї. Через кілька годин двоє людей приходять на зміну, якої насправді ніколи не існувало.

Як виправити баги спільної роботи в реальному часі?

Виправляйте їх по черзі, один за одним: спочатку виявляйте конфлікти запису через інкрементальні збереження, потім об’єднуйте оновлення без втрати локальних правок, а тоді показуйте конфлікти замість того, щоб їх приховувати. Вам не обов’язково ідеально вирішувати спільну роботу в реальному часі з першого дня.

Виправлення 1: Виявляйте конфлікти запису (інкрементальні збереження)

Зробіть так, щоб кожна зміна зберігалась одразу, а не лише по кліку на “зберегти”. Це найважливіше виправлення.

Коли користувач редагує поле, надсилайте зміну у вашу базу даних негайно. Показуйте невеликий індикатор “збережено” або крапку, яка зникає після завершення синхронізації. Якщо друга людина зберігає зміни в той же момент, ваша база даних має обробити це так:

  • Зміна користувача А застосовується першою.
  • Зміна користувача Б застосовується другою.
  • Перемагає користувач Б (перемагає останній запис).

Це жорстко, але чесно: принаймні одна людина побачить, що її зміна не збереглася, і зможе повторити її.

Завдання для розробника: запускайте збереження при кожному натисканні клавіші або через 2 секунди після того, як користувач перестав друкувати, — не по кнопці “Зберегти”. Показуйте індикатор синхронізації. Перевірте це: відкрийте свій застосунок у двох вікнах браузера і відредагуйте те саме поле. Одна зміна має видимо перезаписати іншу.

Виправлення 2: Оновлюйте дані, не втрачаючи локальні правки

Якщо ви опитуєте базу даних кожні 5 секунд (або надсилаєте оновлення через WebSocket), об’єднуйте нові дані без знищення поточних правок користувача.

Неправильний спосіб: перезавантажити всю сторінку. Усі локальні правки зникають.

Правильний спосіб: оновлюйте лише ті поля, які користувач наразі не редагує. Якщо він друкує в полі заголовка — не чіпайте його. Якщо він не торкається дати завершення — оновіть її з сервера.

Завдання для розробника: коли отримуєте свіжі дані з бази даних, об’єднуйте їх: зберігайте локальні правки, оновлюйте все інше. У реальному фреймворку це зазвичай два рядки коду. Перевірте це: відредагуйте одне поле в одному вікні, а інше поле — в іншому вікні одночасно. Обидві зміни мають зберегтися.

Виправлення 3: Показуйте правду чітко

Коли виникає конфлікт або показуються застарілі дані — покажіть це. Не приховуйте.

Приклади:

  • “Це завдання видалила інша людина. Скасувати?”
  • “Поки ви друкували, хтось додав три елементи до цього списку. [Переглянути нове]”
  • “Ви бачите версію дворічної давнини — 2 хвилини тому. Оновіть, щоб побачити останні зміни.”

Завдання для розробника: при завантаженні перевіряйте, чи є у даних, які ви показуєте, часова мітка. Якщо їй більше 30 секунд і користувач намагається редагувати — покажіть попередження і завантажте дані заново. Якщо ви показуєте список, додайте кнопку “Оновити”, яка сприймається як звичайна дія користувача, а не як ознака збою.

Як виглядає повністю вирішена спільна робота в реальному часі?

Золотий стандарт: ми з вами редагуємо спільний документ, я друкую, ви бачите, як рухається мій курсор, і текст з’являється на обох екранах миттєво, причому ніхто з нас не втрачає жодної правки. Для цього потрібні три речі, що працюють разом:

  1. Кожне натискання клавіші зберігається негайно — не чекайте на кнопку.
  2. Конфлікти вирішуються за правилом — якщо ми обидва редагуємо те саме слово, система обирає переможця (зазвичай перемагає останній запис, або вам показують запит на вирішення конфлікту).
  3. Оновлення надходять миттєво — WebSocket, Server-Sent Events або база даних, яка сама надсилає push-оновлення (як-от Firebase).

Більшості застосунків це не потрібне з першого дня. Почніть з інкрементальних збережень (Виправлення 1). Додайте опитування + об’єднання (Виправлення 2), коли застосунком одночасно користуються двоє людей. Додавайте миттєвий push лише якщо конфлікти справді завдають болю.

Як протестувати спільну роботу в реальному часі перед запуском?

Проведіть три тести у двох вікнах браузера перед запуском: тест одночасного збереження, тест застарілих даних і тест оновлення сторінки. У кожного є чіткий критерій “пройдено” або “провалено”.

Тест 1: тест одночасного збереження

  • Відкрийте свій застосунок у двох вікнах браузера.
  • У вікні 1 відредагуйте поле X і збережіть.
  • У вікні 2 одразу після цього відредагуйте поле Y і збережіть.
  • Оновіть обидва вікна.
  • Пройдено: обидві правки на місці. Провалено: одна з правок зникла.

Тест 2: тест застарілих даних

  • Відкрийте застосунок у вікні 1. Не чіпайте його.
  • У вікні 2 змініть щось суттєве (додайте/видаліть рядок, змініть заголовок).
  • Поверніться до вікна 1 (там усе ще показуються старі дані).
  • Спробуйте відредагувати застарілу версію у вікні 1.
  • Пройдено: ви отримуєте попередження, або дані об’єднуються без конфліктів. Провалено: ви перезаписуєте зміну з вікна 2.

Тест 3: тест оновлення сторінки

  • Виконайте значущу незбережену роботу (наполовину заповнену форму, чернетку повідомлення).
  • Оновіть сторінку.
  • Пройдено: ваша робота досі на місці. Провалено: вона зникла.

Зберігати кожне натискання клавіші чи чекати на кнопку “Зберегти”?

Зберігайте кожне натискання клавіші. Це єдине рішення дає вам 80% шляху до спільної роботи в реальному часі — все інше зводиться до того, щоб зробити процес видимим і обробляти колізії.

Користувачі очікують цього вже зараз. Gmail, Google Docs, Slack — усі сучасні застосунки так роблять. Ваш застосунок теж повинен.

Перше, що варто зробити: зробіть так, щоб кожна зміна зберігалась автоматично. Показуйте невеликий індикатор (“зберігається…” — а потім він зникає). Подивіться, що станеться, коли двоє людей редагують одночасно. Якщо правка одного з них зникає — це ваше наступне завдання. Вирішувати одну проблему за раз краще, ніж намагатися побудувати ідеальну спільну роботу з першого дня.