- BrainTools - https://www.braintools.ru -

От закрытого.NET‑шлюза до Java-Kubernetes за 11 часов: production‑кейс агентной разработки

Я довольно активно использую ИИ в разработке примерно с конца 2024 года.

Наверное, многие слышали оценки в духе: «ИИ ускоряет разработчика на 20–30%». Возможно, для каких‑то привычных задач это и неплохая метрика.

Но недавно у меня случился кейс, который вообще плохо укладывается в эту систему координат.

За один очень плотный рабочий день — примерно 11 часов от идеи до боевого переключения — удалось исследовать закрытый Windows‑компонент, восстановить недокументированный бинарный протокол, разобраться с особенностями криптографии, написать совместимую реализацию на Java, сформировать регрессионный корпус из реального трафика, завернуть всё в контейнер, развернуть в Kubernetes, провести независимое ревью и переключить production.

И вот после такого слова про «+30% производительности» начинают казаться немного смешными.

Честно говоря, это пока самый впечатляющий кейс разработки с AI, который у меня был. Восторг скрывать не буду:)

Вводные

Есть некий программный комплекс — назовём его КРЕ.

Его задача — обеспечивать интеграцию бизнес‑процессов компании с внешними информационными системами, в частности с кредитными бюро.

Исторически рекомендуемой платформой для такого контура является Windows.

Если сильно упростить архитектуру, её можно разделить на три части:

  1. непосредственно Java‑приложение КРЕ;

  2. криптографический стек;

  3. небольшой Windows/.NET‑компонент, который дальше я буду называть SSLGate.

Сам КРЕ представляет собой обычное Java web‑приложение, которое можно запустить в Tomcat и достаточно естественно контейнеризировать. Разумеется, при переносе enterprise‑приложений всегда обнаруживаются нюансы, но принципиального технологического блокера здесь не было.

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

Оставался SSLGate.

Функционально компонент достаточно небольшой. Он обеспечивает несколько операций:

  • предоставляет приложению информацию о доступных сертификатах;

  • выполняет операции подписи и проверки;

  • устанавливает защищённые соединения;

  • проксирует сетевые вызовы.

То есть бизнес‑логики внутри практически нет. Но есть одна проблема. Это закрытый .exe, написанный под Windows/.NET. Документации по внутреннему протоколу взаимодействия между КРЕ и SSLGate у нас не было. Вообще.

Зачем было это трогать

Первоначальная задача была довольно приземлённой: повышение отказоустойчивости инфраструктуры.

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

Можно было решить проблему классическим способом: выделить отдельный Windows Server, добавить резервирование, backup, репликацию и продолжать жить дальше.

Но у этого решения есть стоимость.

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

А технологически гораздо приятнее было прийти к более привычной для нас конструкции:

Linux → Docker → Kubernetes → контролируемый deployment → быстрый rollback.

И примерно здесь состоялся такой разговор с Claude:

— Клод, а как ты у нас в reverse engineering? Может, бахнем этот.NET SSLGate с C# на Java за вечер?

— SSLGate, вероятно, не главная проблема. Основной блокер — криптография под Linux.

— Клод, а по криптографии у нас уже есть боевой код под Linux.

— Оу.

Для меня главным неизвестным был SSLGate. Для Claude — криптографический слой.

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

После этого стало очень интересно.

А возможно ли вообще за разумное время воспроизвести поведение [1] закрытого Windows‑компонента настолько точно, чтобы штатное приложение не заметило подмены?

Так задача довольно быстро превратилась в личный инженерный челлендж. Как выяснилось позже — вечер несколько затянулся.

Не coding assistant, а маленькая инженерная команда

Важно оговориться: это не кейс из серии «написал один промпт и ушёл пить кофе».

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

Но существенная разница с обычной разработкой состояла в другом.

Вместо последовательного погружения одного человека во все области вокруг основной сессии Claude была организована небольшая виртуальная инженерная команда из специализированных субагентов.

В разные моменты использовались:

  • крипто‑инженер — криптографический стек, сертификаты, подпись, защищённые соединения;

  • протокол‑археолог — декомпиляция.NET, Java bytecode и восстановление поведения [2] протокола;

  • тестер совместимости — проверка новой реализации штатным клиентским кодом;

  • код‑ревьюер;

  • отдельный независимый финальный review на более сильной модели перед production‑переключением.

То есть фактически одна инженерная задача довольно быстро распалась на несколько специализаций.

В обычной команде здесь легко могли бы встретиться Java‑разработчик,.NET‑разработчик, DevOps, специалист по криптографии, человек с опытом [3] сетевой диагностики и отдельный reviewer.

Я бы не стал утверждать, что AI буквально «заменил шесть человек». Но все эти типы компетенций удалось собрать внутри одного управляемого агентного контура.

Пять ролей: каждая судит по своему источнику истины

Пять ролей: каждая судит по своему источнику истины

И вот это уже намного интереснее, чем просто генерация кода.

Шаг 1. Сначала нужно понять, что вообще делает шлюз

В нашем распоряжении был .NET exe. Среда разработки при этом — Linux.

Первое, что особенно запомнилось: для агента граница технологий практически не выглядела препятствием. Нужно понять .NET‑приложение под Linux? Не проблема. Используем инструменты декомпиляции.

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

Но серверная сторона — лишь половина картины. Чтобы реализовать совместимый компонент, нужно понимать ещё и то, как именно клиент формирует запросы.

Значит, следующий объект исследования — Java‑код внутри вендорского WAR. Где обычного декомпилированного Java было недостаточно, пришлось смотреть уже байткод.

Так постепенно выяснилось, что SSLGate и КРЕ общаются через достаточно компактный собственный бинарный протокол.

Документации нет. Зато есть две реализации: клиент и сервер.

И этого оказалось достаточно, чтобы начать восстанавливать контракт взаимодействия. Не по догадкам, а сопоставляя обе стороны.

При этом всплывали довольно характерные для старых систем нюансы: несколько вариантов представления кадров, различные кодировки для разных частей сообщения, особенности сериализации и необходимость точно зеркалировать формат запроса при формировании ответа.

Технически ничего сверхъестественного. Но именно такие мелочи обычно и съедают дни при попытке воспроизвести закрытый протокол.

Формат кадра запроса и ответа, восстановленный по декомпиляции сервера и байткоду клиента

Формат кадра запроса и ответа, восстановленный по декомпиляции сервера и байткоду клиента

Шаг 2. Когда кода мало — смотрим на провод

Одной декомпиляции оказалось недостаточно.

Можно сколько угодно убеждать себя, что протокол понят правильно, но гораздо надёжнее спросить непосредственно работающую систему.

Поэтому следующим шагом стал пассивный захват реального сетевого взаимодействия между приложением и штатным шлюзом.

Прямо из Kubernetes. Ничего не изменяя в трафике и не вмешиваясь в работу production. Нужен tcpdump внутри контейнера — устанавливаем необходимый инструментарий. PCAP неудобно анализировать вручную — агент пишет небольшой Python‑парсер.

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

Не распарсился очередной вариант сообщения?

Смотрим реальные байты, уточняем гипотезу и запускаем снова. И вот здесь для меня началась одна из самых интересных частей всей истории.

Claude не просто использовал существующие инструменты. Он постоянно создавал себе следующий измерительный прибор, необходимый для очередного эксперимента.

  • Нужен анализатор — появляется анализатор.

  • Нужен monitoring loop — появляется bash‑скрипт.

  • Не хватает клиента БД — появляется небольшая утилита.

  • Нужно сравнить бинарные структуры — пишется очередной parser/diff.

Раньше я воспринимал создание такого одноразового инструментария как отдельную стоимость задачи.

Теперь она начала практически растворяться внутри самого инженерного поиска.

Три источника истины

После захвата трафика у нас оказалось сразу три независимых источника информации [4]:

  1. декомпилированная серверная реализация;

  2. клиентский код приложения;

  3. реальное сетевое взаимодействие между ними.

Если все три источника подтверждают одну модель поведения, степень уверенности резко возрастает.

Из реального трафика позже сформировался регрессионный корпус реальных сценариев, который можно было многократно прогонять уже против собственной реализации.

Важный момент: это принципиально другой подход, чем:

«Кажется, мы поняли, как работает протокол. Давайте задеплоим и посмотрим».

Мы начали строить систему доказательств совместимости.

Три источника истины сходятся в модель протокола, и три независимых подтверждения проверяют реализацию

Три источника истины сходятся в модель протокола, и три независимых подтверждения проверяют реализацию

Самые неприятные несколько байт

Отдельное приключение началось с криптографической подписи.

Первая версия нашей реализации генерировала вполне корректную CMS/PKCS#7-подпись. Но она заметно отличалась от результата штатного SSLGate.

Начали разбирать структуру. Выяснилось, что исходный компонент использует не просто CMS, а CAdES‑BES с набором signed attributes.

Переписали реализацию. Стало гораздо ближе. Но оставалось расхождение буквально в несколько байт. И здесь пришлось уже спуститься на уровень ASN.1/DER.

В какой‑то момент обнаружилось, что исходная реализация явно включает NULL в один из AlgorithmIdentifier, тогда как наша библиотека оставляла параметр отсутствующим.

Те самые:

05 00

Добавили. Структура сошлась.

Голая CMS против CAdES-BES: что именно попадает под подпись

Голая CMS против CAdES‑BES: что именно попадает под подпись

Это был хороший момент. Но ещё лучше оказался следующий тест.

Мы взяли подпись, сформированную нашей Java‑реализацией, и отправили её на проверку самому штатному шлюзу. Он её принял.

То есть исходный production‑компонент выступил независимым oracle и сам подтвердил совместимость нашей реализации.

Вот в этот момент идея «а давайте перепишем закрытый Windows‑шлюз за вечер» перестала казаться исключительно шуткой.

Дальше агентный цикл просто начал разгоняться

После того как основной протокол и подпись были понятны, работа превратилась почти в конвейер.

  • Нужно проверить сертификаты — появляются тесты.

  • Нужно убедиться, что реальный клиент понимает наши ответы — запускаем штатный клиентский класс против нового сервера.

  • Нужно проверить множество живых запросов — replay регрессионного корпуса.

  • Нужно проверить реальные защищённые вызовы — выполняем end‑to‑end тесты.

  • Нужно завернуть решение в контейнер — собирается контейнер.

  • Нужно поднять рядом со старым контуром — появляется отдельный Deployment в Kubernetes.

  • Нужно наблюдать за pod’ом — появляется мониторинг.

  • Подвис CI — временно собираем образ другим способом и не блокируем исследование.

  • В контейнере не хватает диагностической утилиты — устанавливаем её на месте.

  • Нет привычного клиента БД — появляется маленькая JDBC‑утилита.

И так далее. На человеческой скорости каждая такая остановка выглядит небольшой:

«Так, сейчас нужно разобраться с PCAP». «Надо найти, как читать эту ASN.1-структуру». «Надо вспомнить, как устроен этот кусок Kubernetes». «Надо написать тестовый клиент».

Но если таких остановок за день десятки, из них легко собирается несколько дней или недель календарного времени.

Агентная разработка очень сильно схлопывает именно стоимость переключения между областями.

Независимое ревью перед production

К определённому моменту технически всё уже работало.

  • Unit‑тесты зелёные.

  • Криптографические тесты зелёные.

  • Регрессионный корпус проходит.

  • Штатный клиент понимает новый шлюз.

  • End‑to‑end проверки работают.

  • Можно переключать production.

Но перед этим я решил провести ещё один независимый проход по всей реализации отдельным агентом‑ревьюером на сильной модели.

И это решение оказалось полезным. Ревью не нашло critical‑проблем, но обнаружило две high, а также ряд medium и low замечаний.

Среди найденного были вполне реальные вещи: ограничения входных размеров, защита от потенциального исчерпания памяти [5], поведение проверки подписей с цепочкой сертификатов, корректная readiness‑модель и ещё несколько эксплуатационных нюансов.

Согласованный набор проблем исправили. После этого повторно прогнали регрессию.

И получили:

  • unit: 15/15;

  • crypto: 8/8;

  • replay реального корпуса: 48/48.

Всё зелёное. Для меня это ещё один важный элемент нормальной разработки с агентами.

Высокая скорость не отменяет независимое доказательство.

Наоборот, чем быстрее агент способен пройти от идеи до working solution, тем важнее иногда сознательно вставить дополнительную точку остановки:

«Теперь пусть другой агент попробует доказать, что мы ошиблись».

А что с риском?

На этом месте у читателя, наверное, уже возник закономерный вопрос:

«Вы всё это серьёзно проверяли рядом с production?»

Да. Сразу оговорюсь: ни один production при проведении эксперимента не пострадал:)

Риск мероприятия, разумеется, нельзя считать нулевым. Но при работе с такими изменениями важно смотреть не только на вероятность ошибки [6], но и на характер возможного ущерба.

В нашем случае архитектура оказалась очень удачной с точки зрения [7] наблюдаемости ошибки.

  • SSLGate не принимает бизнес‑решений.

  • Он не рассчитывает кредитный лимит.

  • Не меняет данные клиента.

  • Не определяет результат скоринга.

Его ответственность гораздо уже:

  • предоставить сертификат;

  • выполнить криптографическую операцию;

  • установить соединение;

  • передать байты.

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

Ошибка становится заметна сразу. Поэтому основной технический сценарий выглядел достаточно бинарно:

правильно → операция проходит; неправильно → получаем ошибку.

Это существенно отличается от класса ошибок, когда система может неделями незаметно искажать бизнес‑данные, а последствия обнаруживаются постфактум.

Дополнительно новый шлюз разворачивался рядом со старым, а не вместо него. Старый Windows‑контур продолжал существовать как rollback.

Переключение сводилось к изменению точки подключения, поэтому возврат занимал минимальное время. То есть риск не исчезал. Но он был:

  • хорошо наблюдаем;

  • ограничен по последствиям;

  • покрыт тестами;

  • проверен несколькими независимыми способами;

  • обратим.

Именно при таком сочетании я считаю эксперимент приемлемым.

11 часов спустя

К концу дня новая Java‑реализация работала отдельным pod’ом в Kubernetes.

Штатное приложение успешно:

  • получало информацию о сертификатах;

  • выполняло подпись;

  • проходило криптографические проверки;

  • устанавливало необходимые защищённые соединения;

  • воспринимало новый gateway как штатный.

После переключения production само приложение подтвердило подключение уже к нашей реализации.

Старый Windows‑контур при этом остался доступным как быстрый rollback.

От первоначального «а может, перепишем?» до production — около 11 часов очень плотной работы.

К концу этого дня у меня болела спина, мозг [8] уже довольно плохо воспринимал новые идеи, а физическая усталость странным образом перекрывалась очень сильным удовлетворением от результата.

На языке крутилось одно короткое слово, которое хотелось повторять [9] снова и снова. Но для Хабра оно, пожалуй, не очень подходит:)

Почему это не «+30% к производительности»

После этого проекта я ещё меньше верю в формулировку:

«AI делает программиста на 30% быстрее».

Она предполагает старую модель. Есть разработчик. Есть привычный набор его задач. Теперь он пишет те же самые функции, тесты и SQL‑запросы немного быстрее.

Но в моей практике происходит что‑то другое. Меняется сама форма инженерной работы.

За эти 11 часов пришлось пройти через:

  • Java;

  • NET;

  • Java bytecode;

  • reverse engineering;

  • бинарные протоколы;

  • TCP/IP;

  • PCAP;

  • ASN.1/DER;

  • CAdES;

  • криптографический стек;

  • Docker;

  • Kubernetes;

  • CI/CD;

  • базы данных;

  • production debugging.

Раньше практически каждая такая граница компетенции имела стоимость.

  • Не знаешь.NET — изучай или зови специалиста.

  • Не работал с PCAP — остановись и разберись.

  • Не помнишь ASN.1 — снова переключение контекста.

  • Не знаешь конкретную библиотеку — документация, Stack Overflow, эксперименты.

Всё это не исчезло. Но стоимость прохождения этих границ стала намного ниже.

Цикл при этом остался прежним:

гипотеза → эксперимент → факт → решение → следующая гипотеза.

Изменилась цена одного оборота.

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

Теперь померить дешевле, чем угадать.

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

Именно это, а не скорость генерации Java‑кода, оказалось главным ускорителем. Отдельная оговорка, без которой всё выше звучит слишком радужно.

AI способен не только невероятно быстро сделать нужное. Он способен невероятно быстро и очень качественно сделать ненужное, если неправильно поставлена цель.

Утром перед нами был закрытый Windows‑компонент: без документации по внутреннему протоколу, с криптографией, с зависимостью от чужого технологического стека и с реальным production‑контуром за спиной.

Вечером production работал через собственную Java/Linux‑реализацию в Kubernetes. И путь между этими двумя состояниями состоял далеко не из одного написанного сервиса.

Границы задач, которые вообще можно взять на себя, начали резко расширяться. И, пожалуй, именно это сейчас кажется мне самым интересным эффектом AI в разработке.


Вместо послесловия

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

Про агентскую разработку в проде пишу в телеграме: t.me/cto_way.

Автор: korobovn

Источник [10]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/34943

URLs in this post:

[1] поведение: http://www.braintools.ru/article/9372

[2] поведения: http://www.braintools.ru/article/5593

[3] опытом: http://www.braintools.ru/article/6952

[4] источника информации: http://www.braintools.ru/article/8616

[5] памяти: http://www.braintools.ru/article/4140

[6] ошибки: http://www.braintools.ru/article/4192

[7] зрения: http://www.braintools.ru/article/6238

[8] мозг: http://www.braintools.ru/parts-of-the-brain

[9] повторять: http://www.braintools.ru/article/4012

[10] Источник: https://habr.com/ru/articles/1076930/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1076930

www.BrainTools.ru

Rambler's Top100