Как я реализовал Connect RPC на Java с помощью AI-агентов

от автора

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

Введение

Всем привет! Это моя первая публикация, поэтому позволю себе пару слов введения. Меня зовут Иван Федоренков. Я — программист с более чем 20-летним стажем. На сегодняшний день работаю в компании dxFeed на позиции Engineering Manager.

В свободное от работы время я решил проверить, можно ли в одиночку реализовать на Java достаточно сложный RPC‑протокол, не имея команды, большого бюджета и мощного железа. В качестве цели я выбрал Connect RPC, так как этот протокол представляет интерес для моей компании, а кроме того, может быть полезен Java‑сообществу, поскольку полной реализации протокола на Java в открытом доступе мне найти не удалось.

За месяц вечеров и несколько выходных мне удалось собрать спецификацию, подготовить архитектуру, реализовать основные сценарии взаимодействия и довести проект до прохождения полного набора конформанс‑тестов. В статье я расскажу не столько о самом протоколе, сколько о рабочем процессе: как я использовал AI‑агентов, декомпозицию, спецификации, TDD и валидацию, чтобы получить результат, который не стыдно вынести в open source и дальше развивать в сторону production‑ready решения.

Постановка задачи

Сбор спецификации, анализ и реализация протокола Connect RPC на Java.

Спецификация должна быть собрана из трёх источников:

  1. Официальная спецификация на сайте Connect RPC

  2. Референсная реализация протокола на языке Go (Гитхаб)

  3. Конформанс‑тесты (Гитхаб)

Технические требования к реализации:

  1. За основу берётся Netty 4.2.x

  2. Отсутствие зависимостей на другие, сторонние библиотеки, а также отсутствие строгой зависимости на версию Netty (любая совместимая версия должна работать)

  3. Протокол Connect RPC должен быть реализован в виде подключаемых Netty‑хендлеров

  4. Взаимодействие между библиотечными хендлерами и клиентскими хендлерами должно быть реализовано на основе сообщений

Функциональные требования:

  1. Полное покрытие спецификации Connect RPC по всем моделям взаимодействия

  2. Полное прохождение конформанс‑тестов

  3. Расширяемость поддерживаемых Content‑Types и Content‑Encodings

  4. Возможность интеграции с любыми типами сервисов, а также любыми схемами описания сервисов

Мой опыт

Первичный анализ и сбор требований

Время: несколько вечеров.

На первом этапе я приступил к сбору информации вокруг Connect RPC: информация о компании buf, концепция протокола, сценарии использования, спецификация, доступные реализации. Для сбора, структурирования и анализа большого массива информации я предпочитаю использовать думающие модели с максимально возможным контекстным окном, поэтому в данном случае была использована модель Claude Opus Max. Результат я не фиксировал в документе, а скорее сам, в диалоге с нейронкой, погрузился в проблематику, сделал первые предположения о возможных подходах к реализации, наметил декомпозицию задачи.

Структура проекта

Время: один вечер.

На втором этапе был подготовлен скелет проекта. Был создан стандартный Maven‑модуль.

connect-java/├── pom.xml├── CLAUDE.md├── AGENTS.md -> CLAUDE.md├── docs/│   ├── cors.md│   ├── multiprotocol.md│   └── protocol.md└── src/    ├── main/    │   ├── java/io/suboptimal/connectjava/    │   └── resources/    └── test/        └── java/io/suboptimal/connectjava/connect-go/

Пользуясь подходом в духе Obsidian, в отдельный каталог «docs» был загружен весь набор спецификаций с сайта Connect RPC в виде md‑файлов. Это позволило моделям быстрее и дешевле получать необходимую информацию.

Также рядом с проектом была сделана локальная копия реализации протокола на Go. Это сэкономило огромное количество токенов моделям при запросах на сравнение реализаций и уточнение деталей.

В CLAUDE.md было добавлено короткое описание целей проекта, его структуры, основных правил по использованию библиотек и их версий, ссылки на спеки и путь к референсной реализации на Go. Также, для работы с Codex был добавлен симлинк AGENTS.md.

Кроме того, в экспериментальном режиме был подключён Java LSP‑плагин для Claude. LSP — это Language Server Protocol: механизм, позволяющий IDE понимать структуру проекта. Для AI‑агента это даёт возможность быстрее ориентироваться в кодовой базе, находить нужные классы и методы и, по моим субъективным ощущениям, немного снижает потребление токенов.

Подход к реализации

Время: календарный месяц (вечера после работы и некоторое количество выходных дней).

Главная проблема при работе с AI‑моделями в разработке — непредсказуемость результата. Даже сильная модель может сгенерировать рабочий, но плохо вписывающийся в архитектуру код, пропустить граничный случай или раздуть решение лишними абстракциями.

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

Декомпозиция

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

В частности, я разбил Connect RPC на составные части:

  1. Protocol negotiation

  2. Domain model

  3. Compression SPI

  4. Codec SPI

  5. Unary GET

  6. Unary POST

  7. Server Streaming

  8. Client Streaming

  9. Bidi Streaming

  10. CORS

  11. Headers management

  12. Trailers management

  13. Conformance tests

Спецификация

Чрезвычайно важную роль играет детально проработанная спецификация каждого из кусочков (не путать с планом реализации). Для сбора информации и подготовки спеки я использовал самую мощную из доступных моделей — Claude Opus Max. Для Connect RPC использовалось два основных источника: документация на сайте и референсная реализация протокола на Go. Пример промпта: «Изучи, пожалуйста, спецификацию протокола по части Unary GET запросов. Выдели все граничные условия (corner cases) и нюансы. Снабди спецификацию реальными примерами взаимодействия, по аналогии с официальной спекой. Выдели в отдельную табличку все возможные исходы взаимодействия, с указанием кодов ошибок и причин». Затем, поверх этого промпта, я просил сверить получившуюся спеку с Go‑реализацией и внести корректировки там, где мы что‑то упустили или разошлись.

Кроме функциональной спеки, дополнительно, я просил нейронную сеть сгенерировать и отдельную тест спецификацию, которая должна максимально подробно расписывать предполагаемые тест‑кейсы, основываясь на функциональной спеке.

Последовательность важна: сначала функциональная, затем тестовая!

ТДД

Имея на руках подробные спецификации естественным желанием будет попросить нейронную сеть написать план работы и реализовать его, используя, возможно, самую мощную из доступных моделей. Мой же принцип другой: я прошу нейронку накидать скелет тест‑класса, призванного тестировать определённую функциональную область. К сожалению, AI‑модели склонны «фантазировать» креативные решения, раздувать скоуп тестов лишними проверками, не видеть очевидных возможностей оптимизировать тесты, как функционально, так и структурно. Для этой работы я предпочитаю использовать более ограниченные модели, такие как Gpt5.5 High, Sonnet Medium / High (в зависимости от объёма). Такие модели не только потребляют меньше токенов, но и менее склонны к креативным решениям. Иногда, мне приходилось буквально самостоятельно предлагать структуру тестов, чтобы результат соответствовал моим ожиданиям.

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

Реализация фич

На этом этапе, имея на руках спецификации и тесты, я просил думающую модель Opus with xhigh effort составить план реализации. План должен быть максимально подробный, чтобы менее интеллектуальная модель могла его реализовать. Для больших по объёму функциональных областей план должен разбиваться на этапы с контрольными точками. Кроме того, я всегда старался максимально детально расписывать предлагаемый дизайн решения. В то же время, просил модель сделать ревью, указать на слабые места и предложить альтернативы.

Затем, я приступал к реализации. По аналогии с тестами, я предпочитаю отдавать реализацию менее интеллектуальным моделям, таким как Gpt5.5 High, Sonnet High. Такой подход позволяет также экономить токены на рутине — генерации кода.

Субъективно сложилось ощущение, что применение LSP плагина дало положительный результат по части экономии токенов. Также, экспериментировал с Graphify, но особой разницы не заметил и отключил его.

Проверка

После успешной реализации очередной фичи я валидировал результат несколькими способами: просил более интеллектуальную модель сверить спеку, план и реализацию; самостоятельно, в ручном режиме, осуществлял тестовые запросы к серверу, используя curl и buf клиент для Connect RPC.

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

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

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

If the client sends more than one application envelope, Connect Java closes the stream with `invalid_argument` in the EndStreamResponse. Surplus inbound envelopes after the first are a protocol violation; Connect Java must not silently ignore them.

Только на этапе прохождения конформанс‑тестов было выявлено несоответствие и формулировка была изменена:

If the client sends zero application envelopes, or more than one application envelope, Connect Java closes the stream with unimplemented in the EndStreamResponse. Server-streaming requires exactly one inbound envelope; both shortfall and surplus are protocol violations that Connect Java must not silently accept. Surplus envelopes are rejected on arrival of the second envelope, before it is decoded or forwarded to the service. Shortfall is detected at end-of-request-body, before ConnectEndOfStream would otherwise be emitted.

Особенно много расхождений было по части формата ошибок, часть из которых следовало передавать клиенту в виде стандартных HTTP ответов, а часть — в виде End of Stream ответа в JSON формате, чему практически не уделяется внимание в официальной спецификации протокола.

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

Работа с контекстом

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

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

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

Claude vs ChatGPT

По моему субъективному опыту, в разработке на Java на сегодняшний день безальтернативно лидирует Claude, с его моделями Opus / Sonnet. ChatGPT, на мой взгляд, существенно уступает в глубине понимания деталей, в дизайне и качестве кода. Вместе с тем, в условиях ограничений по токенам и имея на руках подробный план, целесообразно делегировать часть задач ChatGPT.

Эти дни

Интересное и неожиданное наблюдение: у моделей тоже бывают плохие дни. Я не знаю точной причины, но по субъективному ощущению, качество ответов иногда заметно меняется даже в рамках одной и той же модели. Поэтому я перестал воспринимать модель как полностью стабильный инструмент и начал явно учитывать текущую «форму» модели в процессе работы. В частности, был день, когда качество ответов Claude Opus очень резко просело и мне пришлось перейти на ручное, пошаговое управление ChatGPT моделью. Через несколько часов, серверы Claude просто упали. Вывод: нужно полагаться на свою интуицию и анализировать качество работы модели в конкретный момент времени. Не стоит ожидать стопроцентной стабильности.

Используемые средства

  1. MacBook Air M3 16 GB RAM

  2. Synology NAS как надёжное хранилище для self‑hosted Git сервера

  3. IntelliJ IDEA Community Edition

  4. Claude подписка $20/мес.

  5. Codex подписка $20/мес.

Результат

В результате эксперимента получилась рабочая Java‑реализация протокола Connect RPC на основе Netty. Проект покрывает все модели взаимодействия Connect RPC: unary‑запросы, streaming‑сценарии, работу с заголовками, трейлерами, кодеками, сжатием и negotiation‑логикой.

Главным критерием готовности для меня было прохождение официального набора конформанс‑тестов.

С результатами работы можно ознакомиться в репозитории на Гитхабе.

Важно подчеркнуть: я не считаю этот проект финальной production‑ready библиотекой. Скорее, это рабочий open source R&D‑прототип, который показывает, что даже сложный протокол можно реализовать силами одного разработчика за ограниченное время, если правильно организовать работу с AI‑агентами: разложить задачу на независимые части, подготовить спецификации, писать тесты до реализации и постоянно валидировать результат.

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

Благодарность

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

ссылка на оригинал статьи https://habr.com/ru/articles/1045542/