Чому згадки ламаються на масштабі
На старті розробки системи згадок усе виглядає тривіально: знаходимо в тексті @, витягуємо список користувачів, підсвічуємо збіги. Проблеми починаються, коли аудиторія переходить від сотень до десятків тисяч, а стрічка — від одного екрана до нескінченного прокручування.
Саме на цьому масштабі розходяться три підсистеми, які в прототипі жили в одній функції: парсинг згадок, автодоповнення користувачів і підсвічування згадок.
Перший збій — розсинхрон підсвічування та посилань. Парсер ріже рядок за межами слів і юнікод-пробілами, а рендер підсвічує за регулярним виразом з іншою логікою меж.
У результаті ім'я з дефісом або точкою підсвічується повністю, а посилання веде на обрізаний нік — користувач бачить одне, переходить на інше.
Лікується це єдиним джерелом істини: парсер віддає не рядок, а список діапазонів «початок-кінець + ідентифікатор користувача», і рендер з побудовою посилань працюють тільки за ним.
Другий збій — затримки автодоповнення. Поки запит іде на сервер на кожне натискання, список підказок приходить із відставанням у кілька символів, і користувач вибирає не ту людину.
Рішення — локальний індекс частих контактів і дебаунс із скасуванням застарілих запитів, щоб відповідь на «@ан» не перетирала відповідь на «@анна».
Третій збій — дублі сповіщень про згадки. При зростанні аудиторії одна подія проходить через кілька обробників: створення поста, редагування, репост, — і кожен шле своє сповіщення. Потрібен ідемпотентний ключ «отримувач + об'єкт + тип події», за яким повторна доставка гаситься.
Розбираємо, як це закривається на рівні архітектури.
Що дає вибудувана система згадок
Швидкість автодоповнення — перше, що помічає користувач. Коли список кандидатів з'являється після введення першого символу, а не через паузу, введення згадки займає менше секунди замість кількох.
Ми тримаємо індекс користувачів і сутностей у пам'яті клієнта та оновлюємо його інкрементально, тому відгук не залежить від розміру довідника — будь у ньому сотні чи сотні тисяч записів.
Точність розпізнавання вирішується на розборі тексту, а не на регулярних виразах.
Парсер враховує межі слів, екранування, вкладені згадки та відмінності розкладок, тому «@іван» і «@Іван» ведуть до одного профілю, а адреса пошти чи нік у посиланні не перетворюються на хибну згадку.
Частка коректно розпізнаних сутностей на розміченому наборі повідомлень виходить до верхньої межі — залишок припадає на одруки, які закриває підказка «можливо, ви мали на увазі».
Навантаження на підтримку падає як наслідок: скарги «згадав не того» і «сповіщення не надійшло» йдуть із потоку звернень, бо вибір із підказки виключає ручне введення імені.
Залученість в обговорення зростає завдяки тому самому механізму — людина, яку відмітили, отримує сповіщення з контекстом і переходить у гілку, а не втрачає повідомлення у стрічці.
Ми збираємо систему на Kotlin і нативному відмалюванні списку підказок, щоб поведінка збігалася на обох платформах.
Метрики — час відгуку підказки, частка успішних переходів за сповіщенням, кількість звернень щодо згадок — виносимо в спільний дашборд, щоб ефект було видно в динаміці, а не на словах.
Підходи до парсингу та автодоповнення: порівняння
Є три робочі схеми, і вибір між ними визначається не смаком, а тим, де в застосунку бекенд і наскільки живий список користувачів.
Якщо довідник невеликий і змінюється рідко, розбір прямо в застосунку дає миттєву реакцію на введення; якщо користувачів десятки тисяч і вони активно перейменовуються, єдине джерело правди — сервер. Гібрид закриває обидва випадки, але потребує акуратної синхронізації.
| Варіант | Коли підходить | Обмеження |
|---|---|---|
| Парсинг на клієнті | Довідник до кількох тисяч записів, змінюється рідко, важлива робота без мережі | Список застаріває між оновленнями застосунку; при зростанні бази зростає пам'ять і час пошуку |
| Серверний розбір | Велика жива база, часті перейменування, єдині правила для всіх платформ | Кожне натискання клавіші — мережевий запит; при поганому каналі підказки запізнюються, потрібен запасний режим |
| Гібридна схема | Довідник великий, але активна частина вузька (листування йде з десятком людей) | Складніша логіка злиття: потрібно вирішувати, чия версія запису свіжіша, і чистити застаріле |
Як вибрати під свій профіль
Ключова ознака — скільки унікальних імен реально потрапляє в підказку за сесію. Якщо це десятки, локальний кеш підказок поверх серверного розбору знімає майже все навантаження: застосунок тримає недавніх співрозмовників і часті контакти локально, а решту дотягує запитом.
Такий кеш оновлюється за часом життя і за фактом відкриття діалогу, тому перейменування підхоплюється без перезапуску.
Що ламається на стику
Головна помилка — розійтися в правилах розбору: клієнт вважає згадкою одну конструкцію, сервер іншу, і в сповіщеннях з'являються биті посилання. Друге джерело збоїв — видалений користувач: запис залишається в кеші, а при надсиланні перетворюється на порожнє посилання.
Тому правила розбору тримають в одному місці, а недавно видалені записи позначають, а не вирізають одразу.
Як ми впроваджуємо систему згадок
Роботу починаємо з аудиту: дивимося, як застосунок поводиться зараз при введенні «@» — де спрацьовує підказка, де текст залишається як є, як зберігаються ідентифікатори згаданих сутностей.
На цьому етапі фіксуємо крайні випадки: згадка на початку рядка, після знака пунктуації, всередині посилання, вставлення з буфера, введення з апаратної клавіатури. Кожен випадок отримує очікувану поведінку — це база для приймання, а не здогадки на око.
Далі проєктуємо схему розбору і збираємо її крок за кроком.
- Аудит поточної поведінки. Проганяємо сценарії введення на живому застосунку, збираємо розбіжності між платформами і фіксуємо, які сутності взагалі можна згадувати.
- Схема розбору тексту. Визначаємо граматику згадки: допустимі символи, межі токена, правила екранування. Розбір іде одним проходом по рядку, тому введення довгого тексту не гальмує.
- Налаштування автодоповнення. Підказка запитується із затримкою після останнього натискання і скасовує попередній запит, якщо користувач продовжує друкувати. Список ранжуємо за збігом і частотою — релевантне опиняється першим.
- Зв'язування з даними. Згадку зберігаємо як ідентифікатор сутності, а не як текст: при перейменуванні користувача чи каналу посилання залишається робочим.
- Перевірка сценаріїв. Проганяємо розбір на підготовленому наборі рядків — з емодзі, змішаними алфавітами, обривом введення, видаленням символу всередині згадки. Окремо перевіряємо поведінку при недоступній мережі.
- Навантажувальна перевірка. Дивимося, як поводиться підказка при великій базі користувачів і на слабкому пристрої: час відгуку і споживання пам'яті.
- Передача документації. Віддаємо регламент розбору, опис формату зберігання згадок і чек-ліст приймання, щоб команда замовника могла розвивати систему сама.
Після передачі залишаємося на підтримці: зміни в правилах розбору або ранжуванні проходять через той самий набір перевірок, що й первинна реалізація.
Артефакти: конфігурації, схеми, політики доступу
Після здачі проєкту у вас залишається не «папка з вихідниками», а комплект, за яким система згадок живе й розвивається без нас. Кожен артефакт закриває свій клас задач: розбір тексту, підказки, права, сповіщення, експлуатацію.
- Схема розбору згадок — формалізований опис того, як парсер знаходить сутності в тексті: межі токена, набір допустимих символів, поведінка на кирилиці та латиниці, обробка вкладених і повторних згадок. Схема прив'язана до тест-кейсів, тому зміна правил одразу перевіряється на регресіях.
- Конфігурація підказок — параметри автодоповнення: поріг спрацювання, розмір видачі, пріоритети сортування, джерела кандидатів (локальний кеш, серверний пошук, недавні контакти). Змінюється без перезбирання застосунку.
- Політики доступу — матриця «хто кого може згадувати»: приватні канали, закриті групи, зовнішні учасники, заблоковані користувачі. Правила перевіряються і на клієнті, і на сервері, щоб обхід інтерфейсу не давав доступу до чужих даних.
- Регламент сповіщень — коли згадка перетворюється на пуш, як групуються повторні, що відбувається при офлайні та як ведеться журнал доставки. Окремо описані сценарії, де сповіщення навмисно не надсилається.
- Документація щодо згадок — специфікація формату зберігання, контракти серверних методів, опис крайових випадків та інструкція для підтримки: як відтворити проблему і що дивитися в логах.
- Схема міграції та відкату — порядок викочування, зворотна сумісність старих версій клієнта, план відключення при деградації.
- Набір автотестів — покриття розбору, підказок і прав доступу, включно з негативними сценаріями.
Комплект передається з репозиторієм і короткою сесією для вашої команди: розбираємо, де що лежить і як вносити правки. Опишіть задачу — повернемося з оцінкою та планом по вашому застосунку.
Кейс: прискорили автодоповнення та прибрали дублікати сповіщень
Клієнт — сервіс спільної роботи зі стрічкою обговорень. Згадки працювали, але підказки з'являлися із затримкою, а під час швидкого набору та редагування користувачі отримували по два-три сповіщення про одне й те саме. Розбираємо кейс згадок: що вимірювали, що змінювали та які метрики до/після отримали.
Що було до
Автодоповнення запитувало повний список учасників при кожному натисканні клавіші та фільтрувало його на клієнті. На активних обговореннях це давало помітний лаг: підказка відмальовувалася, коли користувач уже дописав ім'я.
Сповіщення надсилалися на кожну подію введення — під час редагування тексту або повторної згадки тієї самої людини в одному повідомленні надходило кілька пушів.
Що змінили
- Перенесли фільтрацію на сервер: запит надходить із префіксом після символу
@, відповідь — лише релевантні збіги, обмежені за кількістю. - Додали кеш частих учасників обговорення, щоб типові підказки віддавалися миттєво.
- Запровадили дедуплікацію сповіщень за парою «автор — отримувач — повідомлення»: повторна згадка в тому самому повідомленні не породжує новий пуш.
- Розвели стани введення та надсилання — сповіщення надходить лише після того, як повідомлення реально збережено.
Метрики до/після
| Метрика | До | Після |
|---|---|---|
| Швидкість підказок | помітна затримка під час набору | підказка з'являється практично одразу |
| Дублікати сповіщень | 2-3 пуші на одну згадку | один пуш на повідомлення |
| Помилкові сповіщення | регулярні скарги | зведені до нуля |
Роботу вели наші інженери на React Native — це дозволило перевикористати логіку парсингу між платформами та не дублювати її окремо під кожну ОС.
Після релізу кількість звернень до підтримки щодо сповіщень скоротилася, а частка повідомлень зі згадками зросла: підказки перестали заважати набору тексту.
Чим відрізняється розбір на клієнті від серверного?
Різниця проявляється в момент, коли користувач набирає символ @ і починає вводити ім'я. Клієнтський розбір означає, що застосунок сам знаходить межі згадки в тексті та фільтрує список учасників за вже завантаженим довідником.
Серверний розбір — коли рядок іде на бекенд, там розбирається, зіставляється з базою користувачів і повертається готовий результат. Обидва підходи вирішують одне завдання, але по-різному розподіляють навантаження та затримки.
Клієнтський розбір виграє у швидкості відгуку: підказка з'являється за частки секунди, бо не чекає на мережу. Але довідник учасників потрібно тримати в пам'яті пристрою та синхронізувати — за великих команд це помітна вага й ризик застарілих даних.
Затримки тут мінімальні, зате навантаження на пристрій зростає: розбір тексту, фільтрація та відмальовування списку лягають на процесор телефона, що відчувається на слабких моделях за довгих повідомлень.
Серверний розбір знімає навантаження з пристрою й завжди працює з актуальним довідником — не потрібно нічого кешувати та синхронізувати.
Плата за це — мережева затримка на кожен запит і постійний потік звернень до бекенду: за активного набору це десятки запитів за секунду на користувача. Без грамотного об'єднання запитів і кешу на стороні сервера навантаження на інфраструктуру зростає лінійно разом з аудиторією.
На практиці частіше обирають гібрид: розбір і підсвічування згадок ідуть на клієнті, а пошук і перевірка прав — на сервері. Так затримки залишаються в межах сприйняття, пристрій не перевантажується, а довідник завжди актуальний.
Вартість підтримки при цьому нижча, ніж у чистого серверного варіанта: менше трафіку, простіше масштабування, передбачуване навантаження на бекенд. Вибір диктує сценарій — розмір аудиторії, частота згадок і вимоги до свіжості даних.
Часті технічні сумніви
Хибні спрацювання майже завжди народжуються на стику парсера та правил валідації ніка, а не в самому пошуку збігів. Ми розбираємо рядок посимвольно з урахуванням меж слова та набору допустимих символів, тому адреса пошти, посилання або фрагмент коду не перетворюються на згадку.
Окремо ведемо список винятків — службові слова, емодзі-послідовності, повторні входження в одному повідомленні: кожне правило закривається тестом, і частка помилкових спрацювань тримається на рівні статистичної похибки.
Приватність вирішується на рівні моделі доступу, а не косметикою інтерфейсу. Автодоповнення віддає лише тих користувачів, кого автор повідомлення має право бачити: закриті команди, приховані профілі та заблоковані акаунти не потрапляють у видачу взагалі.
Сервер перевіряє право на згадку повторно в момент надсилання — якщо доступ відкликали між набором і публікацією, згадка не створюється, а автор отримує зрозумілу помилку замість мовчазної втрати.
Що буде зі старими версіями застосунку, які ще в ходу?
Розмітку згадок проєктуємо як розширюваний формат із версіонуванням: старий клієнт не знає про нові типи сутностей, але продовжує коректно відображати вже знайомі. Невідомі конструкції деградують до звичайного тексту, а не ламають повідомлення. Це дозволяє оновлювати застосунок поступово, без примусової міграції всіх користувачів в один день.
Чи не створить автодоповнення пікове навантаження на сервер?
Пошук іде із затримкою введення та скасуванням попереднього запиту, результати кешуються за префіксом, а часті підказки віддаються з пам'яті. При зростанні числа користувачів додаються репліки на читання — видача залишається чутливою, а основна база не отримує зайвих звернень.
Кожен із цих сумнівів ми закриваємо на етапі проєктування, а не після запуску: правила парсингу, модель доступу та формат розмітки фіксуються в документації й покриваються автотестами.
Обговоримо ваше завдання щодо згадок
Щоб оцінка була реалістичною, а не «середньою по ринку», нам потрібно зрозуміти вашу модель даних і сценарії використання згадок. Розбираємо завдання за пунктами: де живуть сутності, як влаштований пошук за ними, які обмеження щодо прав доступу та обсягу довідника.
Далі повертаємося з планом впровадження, строками за етапами та складом робіт — без загальних формулювань.
Ось що ми уточнюємо під час першої розмови:
- Де джерело сутностей — користувачі, команди, документи, проєкти: одна таблиця чи кілька розрізнених довідників, як вони синхронізуються з бекендом.
- Як шукається збіг — за початком рядка, за входженням, за одруками; чи потрібна латиниця поруч із кирилицею і як обробляються складені імена.
- Обсяг і частота — скільки сутностей у довіднику, скільки запитів за хвилину, чи потрібна видача із сервера або достатньо локального кешу на пристрої.
- Права доступу — чи бачить користувач лише своїх колег або весь довідник; як згадка поводиться, якщо в отримувача немає прав на об'єкт.
- Формат зберігання — як згадка лягає в текст повідомлення: ідентифікатор поруч із відображуваним іменем, щоб перейменування не ламало історію.
- Поведінка під час видалення — що показувати, якщо згаданий користувач або об'єкт більше не існує.
- Платформи та інтеграції — чи потрібні сповіщення отримувачу, перехід за тапом у профіль або картку, робота в офлайні.
Після цього ми фіксуємо план впровадження: розмітка моделі даних, парсер тексту, сервіс автодоповнення, інтерфейс підказок, обробка крайових випадків і тести на реальних сценаріях. Строки розбиваємо за етапами, щоб ви бачили, що і коли отримуєте на руки.
Опишіть завдання — де зараз зберігаються сутності, які сценарії згадок критичні та на яких платформах працює застосунок. Повернемося з оцінкою робіт, планом впровадження та строками за кожним етапом.







