Неделя 136. Рождение WhatsApp-канала: микросервис за одни сутки

Неделя 136. Рождение WhatsApp-канала: микросервис за одни сутки

Разбор этой статьи

AI-подкаст BotsellerWhatsApp API с нуля за выходные
0:00 / 0:00

Эту тему разобрали в подкасте. Слушай параллельно с чтением.

Если вы читали ретроспективу недели 137, то помните: там WhatsApp Business API уже приносил деньги - биллинг разговоров, шаблоны, подключение за минуты. Сегодня откручиваем ещё неделю назад - туда, где всего этого не было. В понедельник 9 марта у платформы четырнадцать каналов и ни одного официального WhatsApp. В воскресенье 15 марта в 23:42 ложится последний коммит дня - и канал номер пятнадцать жив: принимает сообщения, отправляет ответы, отчитывается о доставке.

Между этими точками - 266 коммитов в тринадцати репозиториях, новый микросервис, родившийся за одни сутки, вся документация продукта, написанная за три дня, и три критических хотфикса из-за одной-единственной цифры в валидаторе.

Я Дмитрий Дьяконов, основатель Botseller AI. Это пятый выпуск серии «Ретроспектива» - бортжурнал в прошлое, от настоящего к первому коммиту.

Задача недели: WhatsApp по-взрослому

К марту 2026 года Botseller умел общаться в четырнадцати каналах: Telegram в трёх видах, VK, Avito, Viber, почта, виджет на сайт, MAX и другие. Но самый запрашиваемый канал у клиентов - WhatsApp - работал только в неофициальном режиме, со всеми его рисками.

WhatsApp Business API (WABA) - это официальный путь: зелёная галочка верификации, легальные массовые рассылки по шаблонам, стабильность без страха бана. Для бизнеса, который продаёт в переписке, это не опция, а необходимость: неофициальные решения падают в самый неподходящий момент, а официальный API - инфраструктура, на которой можно строить продажи.

Решение было принято в пятницу. План - за выходные провести канал через всю платформу: базу данных, ядро обработки сообщений, биллинг-контур, интерфейс. Звучит авантюрно. Частично так и оказалось - но не там, где мы ждали.

Неофициальный WhatsApp против WhatsApp Business API: риск бана и серые схемы против стабильной инфраструктуры и легальных рассылок

Сорок восемь часов: как строился канал

Суббота - фундамент. Канал в нашей архитектуре начинается с данных: три новые таблицы (подключение клиента, конфигурация, связка с ботом), ограничения целостности, миграции. Затем - регистрация пятнадцатого типа канала в ядре обработки сообщений: входящий поток, исходящие ответы из CRM, хранение конфигураций. К вечеру субботы появилась и таблица шаблонов - WhatsApp разрешает первым писать клиенту только по заранее одобренным шаблонам, без них канал неполноценен.

Воскресенье - шлюз. В 01:07 ночи первый коммит нового микросервиса: шлюз между провайдером WhatsApp Business API и нашей платформой. Каркас лёг сразу целиком - 4227 строк: приём вебхуков, парсер событий, менеджеры очередей, реестр ботов, Docker и CI/CD. К 02:17 - поддержка третьей версии формата вебхуков. Пауза на сон. В 09:03 - производственная обвязка: клиент API провайдера, менеджеры разговоров, подписок и шаблонов, ещё полторы тысячи строк.

Дальше день пошёл слоями: утром - ревью безопасности (убрать персональные данные из логов), днём - критический фикс сериализации, вечером - статусы доставки в CRM, повторные попытки при старте, идемпотентность подписки на вебхуки. В 23:09 лёг первый камень Embedded Signup - встроенного подключения через авторизацию Meta, которое довели до продакшена уже на следующей неделе. В 23:42 - последний коммит. Между первым и последним - 22 часа.

В тот же вечер новый сервис встал под мониторинг Prometheus. Это важная деталь, к которой вернёмся в уроке недели.

48 часов марафона: суббота - фундамент с таблицами и ядром, воскресенье - микросервис от первого коммита в 01:07 до последнего в 23:42

Архитектурный разворот в десять утра

Внутри этого марафона случились два эпизода, которые стоят дороже любых «всё прошло гладко».

Разворот архитектуры. Первая версия шлюза ходила за данными напрямую в сервис данных - так было быстрее написать. К десяти утра воскресенья стало ясно, что это ошибка: шлюз должен разговаривать с ядром платформы, как все остальные каналы, иначе появляется второй источник правды о ботах. Сервис переписали тем же утром. Цена разворота в первый день - один час. Цена того же разворота через месяц, когда на сервис завязаны боевые клиенты, - неделя и риск простоя.

Формат очереди. Сообщения между сервисами у нас ходят через очередь, и ядро ждало бинарный формат сериализации - а новый шлюз отправлял JSON. Сообщения тихо не доходили. Полдня канал «работал» - вебхуки принимались, логи чистые, а ответы не генерировались. Классическая ошибка интеграции: два сервиса согласны в протоколе, но не в формате полезной нагрузки. Нашли, починили одним коммитом с пометкой «критический».

Оба эпизода объединяет одно: они всплыли в первые же сутки, потому что канал сразу гоняли живыми сообщениями. Если бы мы неделю писали код «вслепую» и только потом включили - искали бы то же самое, но в куче в десять раз больше.

Два кейса первых суток: разворот на единый источник правды за час и ошибка формата полезной нагрузки, которую вскрыли живые сообщения

Сага одной константы: «каналов не больше 14»

А теперь самая поучительная история недели. В нескольких местах платформы жила проверка: тип канала - число от 1 до 14. В валидаторах API-схем, в логике подписок, в ограничении на уровне базы данных. Три сервиса, одна и та же граница, зашитая руками.

Канал номер пятнадцать эту границу сломал везде по очереди. Сначала упала валидация при создании подписки - критический фикс. Потом выяснилось, что граница продублирована во втором сервисе - ещё один критический фикс. Потом ограничение в базе данных - миграция. Каждый фикс - однострочный, каждый - найден не поиском по коду заранее, а падением в рантайме.

Туда же в копилку: роутер нового API написали, но забыли зарегистрировать в списке маршрутов - одна строка импорта, из-за которой все эндпоинты канала отдавали 404. И сломанная цепочка миграций базы: два параллельных трека (канал и новый тариф) создали миграции от одной родительской версии, цепочка раздвоилась, пришлось руками восстанавливать линейность.

Ни одна из этих ошибок не стоила больше часа. Но все они - один класс: распределённое знание, которое никто не проверяет автоматически. Граница «не больше 14» - это невидимый контракт между тремя сервисами. Забытая регистрация роутера - контракт между файлом и фреймворком. Раздвоенная цепочка миграций - контракт между двумя ветками разработки. Компилятор молчит, тесты зелёные, падает в проде.

Эффект бабочки от одной константы: граница «каналов не больше 14» зашита в трёх сервисах, канал номер 15 сломал все три по очереди

Урок: фреймворк интеграционного спринта

Главный фаундерский вывод недели 136: новый канал за выходные - это реально, если спринт вертикальный, а грабли - переписаны заранее. После этой недели у нас сложился чек-лист, по которому мы потом подключали каждый следующий мессенджер. Вот он, применим к любой интеграции внешнего API в продукт.

1. Копируй лекало последней интеграции. WhatsApp-канал строился по образцу предыдущего - MAX: та же структура таблиц, те же слои, те же точки подключения к ядру. Первая интеграция стоит дорого, вторая - вдвое дешевле, пятнадцатая - это заполнение шаблона. Если вы делаете вторую интеграцию и не выделили лекало из первой - вы платите за первую снова.

2. Вертикальный срез вместо горизонтальных слоёв. Сначала одно сквозное сообщение: вебхук пришёл, дошёл до ядра, бот ответил, ответ доставлен. Потом обвес: шаблоны, статусы доставки, окна разговоров, онбординг. Мы гоняли живые сообщения с первого дня - и именно поэтому разворот архитектуры и ошибка формата очереди всплыли за часы, а не за недели.

3. Grep-лист перед стартом. Составьте список мест, где зашито знание «сколько у нас сущностей»: валидаторы, перечисления, ограничения БД, конфиги. У нас граница «не больше 14» жила в трёх сервисах - и каждую копию мы нашли падением. Десять минут поиска по кодовой базе до старта экономят три критических хотфикса после. А лучше - единый источник правды: одна константа, из которой генерируются все проверки.

4. Стабилизация в тот же день. Повторные попытки при старте, идемпотентность подписки, дедупликация событий - всё это легло в код вечером первого дня, пока контекст горячий. Отложите «мелочи надёжности» на потом - и будете чинить их по одной, каждый раз заново загружая контекст в голову.

5. Мониторинг - в день релиза, не после инцидента. Новый сервис попал под Prometheus в тот же вечер. Правило простое: сервис без мониторинга не существует. Первый инцидент, который вы увидите из графиков, а не из жалобы клиента, окупает полчаса на настройку.

Этот чек-лист - пять пунктов, ноль магии. Но разница между «интеграция за выходные» и «интеграция за месяц» - именно в нём, а не в героизме.

Плейбук интеграционного спринта: копируй лекало, вертикальный срез, grep-лист до старта, стабилизация в тот же день, мониторинг сразу

Вся документация продукта за три дня

Параллельно с WhatsApp-спринтом шёл второй марафон, тише, но масштабнее по строкам: с четверга по воскресенье мы написали практически всю пользовательскую документацию продукта. Больше тридцати новых страниц, сотня с лишним скриншотов, крупнейший коммит недели - 3695 строк за раз.

Метод оказался важнее объёма:

  • Волны по разделам. Настройка ИИ-бота - одной волной: модель общения, переменные контекста, инструменты CRM, дожимы, типы инструкций, предобработка и постобработка. CRM - тремя волнами: сначала лиды, воронки и диалоги, потом поля, сотрудники и филиалы, потом услуги, абонементы и задачи. Интеграции - по одной: AmoCRM, Битрикс24, Kommo. Каждая волна - завершённый кусок, который можно публиковать.
  • Единый паттерн страницы. Скриншот, шаги, частые вопросы - в одном ритме на всех страницах. Читатель, освоивший одну страницу, ориентируется во всех.
  • Удаление наравне с написанием. Вычистили пустые разделы-заглушки, дубли и стабы. Пустая страница «скоро здесь будет» хуже отсутствующей: она обещает и обманывает. Привычка удалять, которая к неделе 138 выросла в целую стратегию, начиналась здесь.

Зачем это бизнесу. Документация - это не «когда-нибудь потом», это три актива сразу: разгрузка поддержки (ответ ссылкой вместо переписки), поисковый трафик по запросам «как настроить X» и - всё важнее - корм для ИИ-ассистентов, которые советуют инструменты пользователям. Сегодня эта документация живёт в базе знаний Botseller и отвечает на вопросы раньше, чем их успевают задать поддержке.

И честное признание: интерфейс за прошедшие месяцы уехал вперёд, часть скриншотов уже требует обновления. Документация - не проект, а процесс. Но процесс не начнётся, пока нет фундамента, и этот фундамент мы залили за три дня.

Документация как продуктовый актив: 30 с лишним страниц за три дня волнами, единый паттерн страницы и три выгоды - саппорт, трафик, ИИ-ассистенты

Ultra-тир и исчезнувшее имя модели

Маленький эпизод недели с большими последствиями. В пятницу в платформе появился новый верхний уровень ИИ - Botseller Ultra: максимальное качество ответов для сложных сценариев, с прицелом на клиентов, которым важна каждая формулировка.

Любопытная деталь: в первой версии интерфейса рядом с тиром мелькнуло название конкретной модели-провайдера. Через несколько часов отдельным коммитом его убрали. Интуиция подсказала: клиенту важно «максимальное качество», а не бренд модели под капотом, который мы можем сменить в любой момент. Через неделю эта интуиция оформилась в правило - тиры вместо имён моделей по всему продукту. Так и рождаются продуктовые решения: сначала неловкость от лишней детали в интерфейсе, потом принцип.

Рой агентов: запуск сканирования и семь фиксов за вечер

Третий трек недели - «Рой агентов», продукт для работы с Telegram-сообществами, получил главный механизм: пайплайн сканирования. Персоны-агенты читают чаты по расписанию, ИИ решает, где стоит ответить, исполнитель доставляет сообщения. От анализа до доставки - одна цепочка.

В тот же день пайплайн получил двухуровневую архитектуру ИИ: лёгкая быстрая модель делает триаж - стоит ли вообще реагировать на сообщение, - а тяжёлая включается только там, где нужен качественный ответ. Про экономику этого паттерна мы подробно рассказывали в выпуске о неделе 137: 90% событий отсеиваются дешёвым первым уровнем.

Экономика ИИ в Рое агентов: двухуровневый фильтр пропускает через тяжёлую модель только 10 процентов событий

А дальше - честная хроника любого запуска: семь фиксов за вечер. Исполнитель не игнорировал приостановленные миссии - фикс. Дубли действий - фикс. Чаты задваивались в матрице персон - дедупликация. Планировщик не создавал задачи из-за фильтра по идентификатору клиента - фикс. И так до ночи.

Вывод, который я тогда записал: шлейф багфиксов после запуска - это не провал, это фаза. Планируйте её. Релиз в воскресенье вечером означает стабилизацию в воскресенье ночью - либо закладывайте вечер на добивание, либо не запускайте в конце дня. Мы выбрали первое и к полуночи имели работающий пайплайн. Но по-хорошему такие запуски стоит делать утром: те же семь фиксов, только на свежую голову.

Честная хроника запуска сканирования: семь фиксов за вечер - от игнора приостановленных миссий до ошибки планировщика

Фоновые сюжеты: таймзоны, минус 2629 строк и бюджет пять долларов

Таймзон-эпопея. Три дня в середине недели ушло на «простую» задачу: время сообщений в CRM должно показываться в часовом поясе бизнеса, а не в UTC. Пять итераций, один откат, смена подхода с библиотеки дат на нативный Intl, и финал - рефакторинг: вся работа с временем собрана в единый модуль, минус 355 строк разрозненных конвертаций. Правило, которое мы из этого вынесли: если чинишь одно и то же третий раз - это не баг, это архитектура. Третья итерация фикса - сигнал остановиться и рефакторить, а не накладывать ещё одну заплатку.

Минус 2629 строк. В пятницу из интерфейса CRM удалили три старых сайдбара деталей лида - мёртвый код, который полгода лежал «на всякий случай». Каждая строка мёртвого кода - это налог на каждый следующий рефакторинг: её читают, её обходят, об неё спотыкается поиск. Удаление - такая же работа над продуктом, как написание, и как устроен наш ИИ-бот под капотом сегодня - во многом результат таких чисток.

Бюджет пять долларов. В одном из side-проектов парсинг Instagram переехал с самописного решения на внешний API - с жёсткой рамкой: уложиться в пять долларов в месяц. Два дня подстройки расписания и порогов свежести данных под лимит. Микросюжет, но принцип масштабируется: сначала бюджет, потом архитектура. Ограничение в пять долларов дисциплинирует лучше, чем «оптимизируем, когда вырастет счёт».

Фоновые победы недели: таймзоны после пяти итераций собраны в единый модуль, минус 2629 строк мёртвого кода, жёсткий бюджет пять долларов

Был - стал: до и после недели 136

МетрикаДо (8 марта)После (15 марта)
Каналов в платформе1415 (+ WhatsApp Business API)
Шлюз WhatsApp Business APIНетМикросервис в проде, под мониторингом
Шаблоны WhatsApp в CRMНетТаблица + управление из интерфейса
Embedded SignupНетПервые коммиты (продакшен - неделя 137)
Документация продуктаФрагменты30+ страниц, все разделы
Верхний тир ИИНетBotseller Ultra
Сканирование в «Рое агентов»НетПайплайн в проде + двухуровневый ИИ
Работа с временем во фронтендеРазрозненные утилитыЕдиный модуль

Прыжок платформы за семь дней: от 14 каналов и фрагментов документации к WhatsApp Business API под мониторингом и полной базе знаний

Цифры недели 136

МетрикаЗначение
Коммитов всего266
Репозиториев с активностью13
Время жизни микросервиса от первого коммита до продакшена22 часа
Строк в первом коммите шлюза4227
Критических хотфиксов из-за константы «не больше 14»3
Новых страниц документации30+
Крупнейший коммит документации3695 строк
Фиксов после запуска сканирования7 за вечер
Удалено мёртвого кода в CRM2629 строк

Что я сделал бы иначе

Ретроспектива честна, только если в ней есть эта секция.

Прошёлся бы поиском по границам до старта, а не после падений. Три критических хотфикса из-за «не больше 14» - это три раза по одному и тому же классу ошибки. Grep-лист распределённых констант теперь обязательный пункт перед любой интеграцией - но родился он из падений, а мог из десяти минут подготовки.

Собрал бы роутер целиком до выката, а не порциями. Цепочка «добавили роутер - сломался импорт - забыли регистрацию» на живом сервисе выглядела как мигающий продакшен. Локальная сборка и один смоук-тест сэкономили бы три хотфикса.

Запустил бы сканирование утром, а не вечером воскресенья. Семь фиксов на уставшую голову - плохая лотерея. Повезло, что все оказались простыми.

А вот сам формат - вертикальный спринт выходного дня с живыми сообщениями с первого часа - оставил бы без изменений. Именно он вытащил на поверхность разворот архитектуры и ошибку формата очереди за часы. Медленная «правильная» разработка нашла бы их через месяц, в куда более дорогой момент.

Цена скорости и уроки на будущее: grep до старта, локальная сборка эндпоинтов, релизы только утром - и неизменный вертикальный спринт

В следующем выпуске ретроспективы - неделя 135: массовые рассылки, система прав сотрудников и большой редизайн миссий.

Создавайте продукты быстро, но осознанно: ретроспектива Botseller, неделя 136


Это серия «Ретроспектива Botseller» - путешествие от первого коммита до рабочего продукта. Читайте в любом порядке. Подписывайтесь на Telegram-канал, чтобы не пропустить новые выпуски.

FAQ

Что такое WhatsApp Business API и чем он отличается от обычного WhatsApp?

WhatsApp Business API (WABA) - официальный интерфейс Meta для бизнеса: верифицированный профиль с зелёной галочкой, легальные массовые рассылки по одобренным шаблонам, работа через провайдеров без риска блокировки. Обычный WhatsApp и неофициальные обвязки к нему таких гарантий не дают: аккаунт могут заблокировать в любой момент вместе со всей историей переписки с клиентами.

Сколько времени занимает интеграция нового мессенджера в платформу?

Зависит от того, какая это интеграция по счёту. Первая - недели: вы платите за архитектурные решения. Но если из первой интеграции выделено лекало - структура данных, слои, точки подключения к ядру, - то каждая следующая становится заполнением шаблона. Наш пятнадцатый канал прошёл путь от решения до работающего продакшена за одни выходные.

Как избежать ошибок при добавлении нового типа сущности в микросервисную архитектуру?

Главный источник ошибок - распределённое знание: границы и перечисления, продублированные по сервисам (валидаторы, схемы, ограничения БД). Перед стартом составьте список всех мест, где зашито «сколько у нас типов», и обновите их одним заходом. Идеал - единый источник правды, из которого проверки генерируются автоматически: тогда новый тип добавляется в одном месте.

Зачем SaaS-платформе полная документация продукта?

Это три актива одновременно: разгрузка поддержки (ответ ссылкой вместо получасовой переписки), поисковый трафик по запросам «как настроить» и материал для ИИ-ассистентов, которые всё чаще советуют инструменты пользователям. Документация без фундамента не начнётся сама: эффективнее выделить спринт и написать все разделы волнами по единому паттерну, чем годами дописывать по странице.

Что такое двухуровневая архитектура ИИ и когда она нужна?

Это разделение на дешёвый триаж и дорогой ответ: лёгкая быстрая модель фильтрует поток событий и решает, где нужна реакция, а тяжёлая модель включается только на отобранных случаях. Паттерн окупается везде, где событий много, а реагировать нужно на малую долю: мониторинг чатов, обработка обращений, модерация. Расход на инференс падает в разы без потери качества ответов.

Почему важна идемпотентность при работе с вебхуками?

Провайдеры повторяют вебхуки при сбоях и таймаутах - одно событие может прийти дважды или трижды. Без идемпотентности это означает дубли: два ответа клиенту, двойное списание, задвоенная подписка. Правило: каждая операция, запускаемая вебхуком, должна безопасно переживать повтор - дедупликация по внешнему идентификатору события и проверка «не сделано ли уже» перед выполнением.

Стоит ли делать большие релизы в выходные?

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