Неделя 129. Ставка на двух лошадей: своя CRM и чужая одновременно, подписки и трещина, которую я датировал неверно

Разбор этой статьи
Эту тему разобрали в подкасте. Слушай параллельно с чтением.
Начну с признания. В прошлом выпуске, про неделю 130, я назвал один баг «первой трещиной» в изоляции данных. Красивая фраза, и она была неправдой. Настоящая первая трещина случилась неделей раньше, вот на этой неделе, и я про неё в том выпуске умолчал. Не нарочно: просто когда пишешь свою историю, всегда хочется, чтобы первый звонок прозвенел чуть позже, чем на самом деле. Ретроспектива хороша тем, что не даёт себя обмануть: git log помнит точные даты, даже когда память основателя округляет их в свою пользу.
Я Дмитрий Дьяконов, основатель Botseller AI. Это одиннадцатый выпуск серии «Ретроспектива», бортжурнал в прошлое: от настоящего к первому коммиту. Сегодня отматываю на 19-25 января 2026 года. Если неделя 130 была тихим кануном, а неделя 131 громким взрывом с тремя ставками, то неделя 129 была самой странной из трёх: 89 коммитов, и почти все они делали одновременно две вещи, которые логика велела делать по очереди. Мы строили свою CRM и глубоко врастали в чужую. В одну неделю. Об этом и рассказ.
Контекст: что было к началу недели
Середина января 2026 года. Продукту полгода, боты работают, есть первые платящие клиенты, но денег продукт берёт с них коряво: разовые списания, ручные сверки. Своя CRM пока прототип. Интеграции с чужими CRM есть, но поверхностные. И главное, у меня в голове ещё нет того ответа, который появится через две недели: строить своё или жить на чужом. Я его не знаю. И поэтому, как выяснится, всю эту неделю я голосую за оба варианта сразу.
Два фронта: своя CRM и чужая в один и тот же четверг

Самое удивительное в неделе 129 видно только на общей картине коммитов. В четверг 22 января я строил свою CRM: обвязку с сервисом данных, бизнес-логику воронок и статусов лидов, создание переменных и сущностей при рождении лида. В тот же четверг, буквально соседними коммитами, я строил полную поддержку чужой CRM, Битрикс24: типы, фильтры, синхронизацию статусов, поддержку на бэкенде и фронте. Два больших дела, каждое тянуло на два десятка коммитов, и оба шли параллельно, как будто я не мог решить, за какую команду болею.
А я и не мог. В этом вся честность момента. Через две недели, на неделе 131, я сформулирую принцип «лид рождается в переписке» и выберу свою CRM. Но принцип этот родится не из размышлений в кресле, а вот из этих двух параллельных фронтов. Пока я строил глубокую интеграцию с Битрикс24, я на своей шкуре чувствовал, что значит жить на чужой территории: подстраивать свои сущности под чужие, ловить их граничные случаи, чинить дубли статусов из-за их особенностей. И одновременно, строя свою CRM, я чувствовал, каково это, когда всё под твоим контролем. Неделя 129 была не колебанием труса. Она была экспериментом, из которого выросла убеждённость.
Развилка, которую нельзя решить размышлением

Здесь главный урок недели, и я вынесу его вперёд, потому что он для меня оказался самым дорогим. Я долго думал, что серьёзные решения принимаются так: садишься, взвешиваешь за и против, выбираешь. За полгода продукта я понял, что это миф. Настоящие решения о продукте приходят из рук, а не из головы. Ты не можешь узнать, каково быть плагином к чужой системе, читая про это. Ты узнаёшь, только построив плагин и почувствовав его потолок. Ты не можешь взвесить свою CRM на весах, пока не подержал её в руках.
Поэтому неделя двух фронтов, которая со стороны выглядит как нерешительность, на деле была самым быстрым способом принять решение. Я заплатил за неё двойной работой, это правда: обе ветки требовали внимания, и обе двигались медленнее, чем если бы я вложился в одну. Но купил я то, что размышлением не купить: точное знание, где мой дом. Если вы сейчас стоите перед развилкой «своё или чужое» и не можете выбрать за столом, мой совет из прожитого: не выбирайте за столом. Постройте по-маленькому обе ветки и послушайте, какая из них на ощупь ваша. Решение придёт само, и оно будет твёрдым, потому что оно из опыта, а не из презентации.
И чтобы не создавать ложного впечатления: чужая ветка не пропала зря. Мост к Битрикс24, достроенный на следующей неделе, работает до сих пор, и отлаживать его мы будем ещё не раз. У нас и гайд по интеграции с Битрикс24 живёт. Просто из двух ветвей одна стала домом, а другая дорогой. Понять, что есть что, можно было только построив обе.
Вторая большая стройка: продукт учится брать деньги правильно

Пока два CRM-фронта делили мои дни, фоном шла третья стройка, самая денежная: система подписок на ботов и AI-сотрудников. Шестьдесят семь файлов, почти семь тысяч строк в сервисе данных. До этой недели продукт брал деньги как умел: разово, вручную, с ошибками. Теперь у платного доступа появился жизненный цикл: подписка, её идентификатор, аудит операций, единая логика для всех типов ботов.
Почему именно эта неделя? Потому что первые платящие клиенты уже были, и каждый следующий превращал кустарный биллинг в мину. Одно дело сверять оплаты вручную на десяти клиентах, другое на сотне. Систему подписок мы строили не потому, что клиентов было много, а потому, что их вот-вот должно было стать много, и чинить фундамент под нагрузкой куда дороже, чем до неё. Это ровно тот принцип «покупки будущей скорости», который я разбирал в прошлом выпуске: тихая инфраструктурная работа, которая окупается, когда приходит рост.
Суббота, кстати, началась с расплаты за спешку: первый же боевой сценарий покупки подписки упал с ошибкой 500. Починили в ночь. Это нормальный налог на скорость, и я давно не расстраиваюсь: если новая система не икнула на первом реальном сценарии, значит, вы слишком долго её полировали в вакууме вместо того, чтобы отдать людям.
Трещина, которую я датировал неверно

Теперь та самая исповедь из начала. В пятницу 23 января лёг коммит с пометкой security, закрывавший уязвимость класса IDOR в нашем CRM API. Простыми словами: любой авторизованный пользователь мог поменять один параметр в запросе, идентификатор заказчика, и получить доступ к чужим данным. Не взлом, не подбор пароля. Просто система верила параметру из запроса и не проверяла, а имеет ли этот человек право на этого заказчика.
Починили правильно: перед каждым запросом теперь идёт проверка доступа через отдельный сервис, а если сервис вдруг недоступен, доступ запрещается, а не разрешается. Это называется fail-close, и это ровно то поведение по умолчанию, за которое я потом буду агитировать в выпуске про неделю 132: при сомнении система закрывается, а не открывается.

А теперь честная арифметика дат. В выпуске про неделю 130 я торжественно назвал «первой трещиной» изоляции данных один баг от 29 января. Но вот же она, настоящая первая трещина, от 23 января, неделей раньше, того же самого рода: эндпоинт, доверяющий параметру доступа без проверки. Я про неё в том выпуске не вспомнил. И это идеальная иллюстрация того самого правила «ищи сёстры бага», которое я оттуда же и проповедовал. Дефект изоляции данных был не одиноким случаем и не парой случаев. Это была семья, растянутая на недели, и писала её одна голова, моя, в одном и том же стиле «сначала функциональность, потом проверка прав». Урок из этой пары трещин я усвоил дорого: если вы нашли место, где система верит пользователю на слово, не чините его в одиночку. Заведите отдельный день и пройдите все похожие места разом. Они есть. Я гарантирую.
Ещё одна честность: SSL, который мы выключили

В ту же пятницу есть коммит, которым я странным образом горжусь, хотя технически он про то, как мы ослабили безопасность. Один наш сервис не доверял сертификату другого нашего сервиса, и вызовы падали. Быстрое решение: отключить проверку сертификата. На данный момент безопасно, потому что вызовы и так защищены секретным токеном. Но рядом, прямо в коде, лёг честный список того, что здесь надо будет сделать правильно: белый список адресов, взаимные сертификаты, приватная сеть, защита от перебора токена.
Горжусь я не заплаткой, а тем, что мы записали долг вслух, а не спрятали. Технический долг это не стыд, это нормальная часть скорости. Стыдно не иметь долгов, а иметь тайные: те, о которых знал автор и не сказал никому, и которые всплывают через полгода как «а почему у нас тут дыра?». У всякой заплатки должен быть ценник, повешенный на видное место. Мой рефлекс с той недели: если ставишь костыль, тем же коммитом пиши, чем он опасен и как его убрать. Иначе костыль превратится в фундамент, а это худшее, что бывает с костылями.
Способность расследовать: журнал, рождённый из пропажи

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

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

Две детали я по привычке храню как экспонаты. Первая: у одного клиента воронка распухла до двухсот тридцати шести статусов, а код синхронизации по недосмотру брал из базы только первые сто. Всё, что за сотней, для него не существовало, и он бодро плодил дубликаты при каждом обновлении. Лечится одной цифрой, но диагностируется мучительно: баг проявляется только у того, у кого данных больше, чем у всех остальных. Урок на полях: любые «магические сотни» в запросах это отложенная бомба, которая ждёт вашего самого крупного клиента.
Вторая деталь про смирение. На неделе была попытка оптимизировать авторизацию одного из сервисов, и её пришлось откатить обратно к предыдущей стабильной версии: новая не запускала боты, а нужный образ оказался недоступен в реестре. Откат это не поражение. Откат это встроенный тормоз, которым здоровая команда пользуется без стыда. Хуже отката только его отсутствие: когда назад дороги нет, и ты чинишь горящее в проде вместо того, чтобы спокойно вернуться на шаг.

Урок недели: убеждения приходят из рук

Каждый выпуск я стараюсь заканчивать одним уроком, пережившим свою неделю. У недели 129 он про то, как принимаются решения, которые потом определяют продукт на годы.
Я мог бы рассказать вам красивую версию: мол, в январе я стратегически проанализировал рынок, понял, что будущее за собственной CRM, и целеустремлённо пошёл к цели. Это была бы ложь того же сорта, что «первая трещина случилась 29 января». Правда скучнее и полезнее: я две недели строил обе ветки, потому что не знал, какая правильная, и узнал это только руками. Убеждённость, которая на неделе 131 выглядит как смелое решение, на неделе 129 была ещё сырой глиной, которую я мял с двух сторон.
Практический вывод, если вы ведёте свой продукт и застряли на большой развилке. Перестаньте искать ответ в размышлениях, если он не находится за разумное время. Разрешите себе неделю двух фронтов: постройте обе ветки по-маленькому, по-настоящему, до первого честного ощущения. Да, вы заплатите за это двойной работой. Но вы купите то, чего не даёт никакой анализ: телесное знание, где ваш дом. А решение, принятое телом, потом не расшатывается сомнениями, потому что за ним стоит не аргумент, а опыт. Лучшие мои решения устроены именно так: сначала руки, потом голова, и только в самом конце красивая формулировка для блога.
Цифры недели

- 89 коммитов в девяти проектах: самая плотная неделя зимы
- Около 26 тысяч добавленных строк кода без учёта документации
- 2 больших фронта параллельно: своя CRM и полная поддержка Битрикс24
- 67 файлов и почти 7 тысяч строк: система подписок на ботов
- 183 строки: закрытие IDOR-уязвимости в режиме fail-close
- 4 пункта технического долга, записанные вслух в одном коммите про SSL
- С 4 секунд до 100 миллисекунд: список ботов после схлопывания 14 запросов в 1
- 236 статусов у одной воронки против лимита в 100: баг, ждавший самого крупного клиента
Что было дальше
Дальше была неделя 130: тихий канун большого взрыва, где мост к Битрикс24 обрёл самообслуживание, а связка ядра и CRM легла в 06:19 утра. Ещё через неделю, на неделе 131, два фронта этой недели встретят свою развязку: я выберу свою CRM, и выбор этот будет твёрдым именно потому, что обе ветки я построил здесь, руками. А трещина изоляции данных, честно передатированная в этом выпуске, аукнется большой дырой через две недели, и только тогда я наконец пройду все её места разом.
Все выпуски серии собраны в категории ретроспектива, а текущая хроника разработки продолжается в бортовом журнале.
FAQ
Что такое ретроспектива Botseller?
Серия «Ретроспектива», бортжурнал в прошлое: от настоящего к первому коммиту. Каждый выпуск разбирает одну неделю разработки платформы по реальным git-логам, с фокусом на развилки: почему выбрали именно эту задачу, какие были альтернативы и как решение развернулось спустя месяцы. Все выпуски собраны в категории ретроспектива.
Своя CRM или интеграция с чужой: как понять, что выбрать?
Наш опыт: это решение не берётся за столом, его берут руками. Мы неделю строили и свою CRM, и глубокую интеграцию с Битрикс24 параллельно, и именно ощущение от обеих веток дало ответ. Если у бизнеса уже всё живёт в большой CRM, разумнее интеграция; если продажи идут в мессенджерах, а отдельной CRM нет, встроенная выигрывает. Разбор аргументов есть в выпуске про неделю 131 и в статье про интеграцию с Битрикс24.
Что такое IDOR-уязвимость простыми словами?
Это когда система пускает вас к данным по идентификатору из запроса, не проверяя, ваши ли это данные. Поменял чужой идентификатор в адресе, и видишь чужое. Защита строится на обязательной проверке прав перед каждым запросом и на принципе fail-close: если проверить право прямо сейчас нельзя, доступ запрещается, а не разрешается. Родственный случай мы разбираем в выпуске про неделю 132.
Зачем стартапу система аудита изменений?
Чтобы иметь возможность расследовать инциденты. Пока изменения не логируются, любой сбой превращается в тупик: непонятно, кто, что и когда поменял. Аудит, который сам вычисляет разницу до и после для ключевых сущностей, даёт эту память. Важно делать его отказоустойчивым: сбой в самом логировании не должен ронять основную операцию, иначе защитный механизм станет источником проблем.
Как правильно относиться к техническому долгу в стартапе?
Технический долг неизбежен, когда важна скорость, и это нормально. Опасен не долг, а тайный долг: костыль, о котором знает только автор. Наше правило: если ставишь заплатку, тем же коммитом пиши, чем она опасна и как её убрать. Долг, записанный вслух, управляем; долг, спрятанный в голове одного человека, однажды всплывает как авария. Ставьте на каждый костыль видимый ценник.
Почему интерфейс тормозит и всегда ли нужен сервер мощнее?
Почти никогда не нужен. Чаще всего медленно означает, что данные собираются десятком запросов там, где хватило бы одного. Мы схлопнули список ботов с четырнадцати параллельных запросов в один и ускорились с четырёх секунд до сотни миллисекунд, не трогая железо. Скорость интерфейса это в первую очередь вопрос архитектуры запросов и уважения к времени пользователя, а не мощности сервера.



