Инженерия

Шесть копий одного кода: интеграции в один контур

← Все статьи

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

К шестой интеграции это становится основным источником дефектов. Мы прошли этот путь и потратили заметную часть релиза на то, чтобы его развернуть.

Что накопилось

К моменту рефакторинга картина выглядела так:

Было Стало
Шесть копий приёма входящего события Один входящий путь
Пять исполнителей опроса источников Один runtime опроса
Четыре списка регистрации модулей Один composition root
Ссылки на секреты разбирались в каждом адаптере Один владелец ссылок на секреты
Каталог типов жил отдельно от кода Проекция контракта
Роль в чате — строковые литералы Роль объявляет сам адаптер
Представление для интерфейса размазано по слоям Один владелец представления

Каждая строка здесь — не эстетическая претензия, а конкретный класс инцидентов.

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

Контракт вместо каталога

Самое полезное изменение оказалось самым неочевидным: каталог типов интеграций перестал быть отдельной сущностью и стал проекцией контракта.

Раньше это были две независимые вещи: код адаптера и описание того, что этот адаптер умеет, — в отдельном списке, который надо не забыть обновить. Расхождение между ними обнаруживалось пользователем.

Теперь описание выводится из самого контракта. Адаптер объявляет свои возможности, свою роль в чате, свою схему настроек — и интерфейс, документация и валидация читают одно и то же. Рассинхронизироваться нечему.

Отсюда же выросла способность отдавать структурное описание интерфейса модели: если адаптер описывает себя сам, это описание можно передать агенту, и он будет работать с фактическим контрактом, а не с предположениями о нём.

Что появилось вместе с единым контуром

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

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

Корреляция и контекст наблюдения. Каждая операция адаптера несёт контекст, по которому её видно в телеметрии. Единый контур означает, что это не нужно вспоминать в каждом адаптере отдельно.

Живые статусы очередей. Интерфейс показывает фактическое состояние опроса, а не заранее записанное «в порядке». Отдельное неприятное открытие рефакторинга: старый интерфейс показывал зелёное там, где давно было красное.

Вывод интеграции из эксплуатации. Раньше отключение означало «удалить запись в базе и надеяться». Теперь это полноценная операция с очисткой — потому что есть единое место, которое знает, что именно надо очистить.

Политика хостов. Для трекера — явный список разрешённых адресов вместо «куда настроили, туда и пойдём».

Побочное следствие: онбординг делает человек

Отдельный вывод, который стоит зафиксировать. В какой-то момент у нас интеграции мог создавать агент-воркер: увидел задачу, завёл подключение, пошёл работать.

Звучит удобно ровно до первого случая, когда агент заводит подключение к трекеру с некорректным адресом — и это уходит в рабочий контур. Создание интеграции — это конфигурация периметра, а не рабочая операция. Такое делает человек.

Мы вынесли это в явное правило: воркер работает внутри выданных доступов и не расширяет их сам. Расширение прав — отдельная операция с записью в журнал аудита и подтверждением владельца.

Что делать, если вы на пятой копии

Практический порядок, который сработал у нас:

  1. Сначала входящий путь. Он самый повторяемый и даёт наибольшую отдачу — дедупликация, корреляция и защита от циклов оказываются в одном месте.
  2. Потом регистрация. Один composition root вместо списков. Это скучно и механически, но убирает целый класс «работает наполовину».
  3. Потом секреты. Один владелец ссылок на секреты — предпосылка к тому, чтобы вынести их в защищённое хранилище, а не держать в переменных окружения.
  4. Контракт — в конце. К этому моменту уже видно, что у адаптеров действительно общего, и обобщение получается по факту, а не по догадке.

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

Контакты

Начните с пилота на одной команде

Расскажите про стек и текущий процесс — вернёмся с оценкой эффекта и планом внедрения.

Электронная почта
Режим работы
Пн–Пт, 10:00–19:00 МСК

Укажите телефон или e-mail. Форма подготовит письмо — отправьте его в своём почтовом клиенте.