惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
B
Blog
Last Week in AI
Last Week in AI
Jina AI
Jina AI
WordPress大学
WordPress大学
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
宝玉的分享
宝玉的分享
T
Tailwind CSS Blog
P
Proofpoint News Feed
酷 壳 – CoolShell
酷 壳 – CoolShell
Google DeepMind News
Google DeepMind News
U
Unit 42
雷峰网
雷峰网
J
Java Code Geeks
Apple Machine Learning Research
Apple Machine Learning Research
MongoDB | Blog
MongoDB | Blog
F
Fortinet All Blogs
美团技术团队
Y
Y Combinator Blog
腾讯CDC
B
Blog RSS Feed
T
The Blog of Author Tim Ferriss
Microsoft Azure Blog
Microsoft Azure Blog
Recent Announcements
Recent Announcements

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания
Миграция микросервисов на Python с помощью LLM: экономим ...
deadrain (Ци · 2026-05-26 · via Все публикации подряд на Хабре

Время на прочтение6 мин

Охват и читатели5.1K

Кейс

Привет, Хабр! Меня зовут Михаил, в Циане я занимаюсь развитием культуры и developer experience. Архитектура у нас микросервисная, за каждый микросервис отвечает конкретная команда. В любой команде обычно есть микросервисы, которые помогают ей достигать собственных целей, и микросервисы, которые достались по наследству.

Наш бэкенд написан на Python и C#, и иногда в одной команде используются микросервисы на двух языках. Я считаю, что это не самый удобный расклад: лучше все-таки иметь один стек в рамках одной команды. Если, например, в команде с питонистами и единственным шарпистом последний уходит в отпуск, то при поломке сервиса на C# остальной команде придется этого шарписта ждать. Либо срочно вызывать на подмогу другого шарписта.

Можно переписать все микросервисы на один язык. Довольно трудоемкая задача, если заниматься этим вручную. Разработчику нужно погрузиться в микросервис, максимально покрыть тестами бизнес-логику и аккуратно все переписать. Не забывая, что делать один в один нужно не всегда, поскольку архитектурные паттерны Python и C# различаются.

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

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

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

Разделение по этапам и стейт-машина

Я начинал создавать скилы с самого простого промпта: вот тебе микросервис на C#, построй план переноса и перепиши его на Python. Разумеется, в итоге ничего не заработало, потому что LLM не знала ничего ни про нашу платформу, ни про наш SDK. Это нужно рассказать LLM дополнительно. Это понятная и не самая интересная часть работы.

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

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

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

Подготовка микросервиса к миграции

Первый скил — это подготовка микросервиса. Здесь LLM анализирует микросервис и составляет реестр всех внешних эндпойнтов — точек, через которые можно с микросервисом взаимодействовать: HTTP API-ручки, отчетные события, фоновые задачи. Каждый эндпойнт, по сути, инкапсулирует кусочек бизнес-логики. В данном случае нейросеть насчитала у микросервиса 64 эндпойнта.

Далее по каждому эндпойнту LLM делает вот что:

  1. Полностью анализирует его бизнес-логику и по итогам строит граф с описанием этой логики на более высоком уровне, чем написан код.

  2. Валидирует этот граф, сопоставляя его с кодом, при необходимости правит граф.

  3. Оценивает имеющиеся тесты эндпойнта: насколько и как качественно они покрывают граф.

  4. Пишет тест-планы по непокрытым частям эндпойнта и снова проверяет тесты.

Обработав все эндпойнты, LLM запускает итоговые функциональные тесты. Так удачно сложилось, что фреймворк функциональных тестов у нас един для Python и C#. В нем микросервис тестируется как черный ящик, поэтому язык значения не имеет.

Миграция с C# на Python

Далее вступает в дело скил миграции. Он готовит шаблон микросервиса на Python, прогоняет без изменений функциональные тесты C# и смотрит, что они падают именно на бизнес-логике — ошибка 404 или отсутствие ручки. Так мы понимаем, что инфраструктурных проблем из-за самого микросервиса нет.

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

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

Для такой проверки у нас есть отдельный инструмент, так что мы можем не раздувать контекст LLM правилами. Скил отправляет код в наш линтер, забирает ответ и чинит по нему код. Инструкция для LLM получается очень простой, и нам не нужно углубляться в промпт-инжиниринг. Кроме того, мы по своему опыту знаем, что от запуска к запуску LLM может интерпретировать контекст по-разному: сегодня что-то учтет, а завтра уже нет. Отдельная же проверка через наш инструмент обеспечит здесь стабильность: правила у нас вполне детерминированные, и скил будет прогонять проверки, пока не получит одобрение от нашего линтера. Для контекста LLM лучше оставлять правила, которые работают не всегда, а в зависимости от ситуации. 

Полная проверка и передача кода

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

Что может пропустить LLM

С помощью LLM мы пока мигрировали два микросервиса, и на первом столкнулись с проблемой: iOS-приложение при обращении к сервису начало получать ошибку. Почему это стало неожиданностью? Потому что все события при раскатке микросервиса — в HTTP API на эндпойнтах — попадают в Swagger, в некоторый контракт. Этот контракт есть у самого микросервиса, и все пользователи микросервиса обращаются к нему через клиента, код которого сгенерирован на основе этого самого контракта.

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

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

Так вот, в истории с iOS-приложением мы успешно прогнали этот цикл и убедились, что никаких изменений по контрактам нет. Но потом выяснилось, что в iOS-приложении есть какой-то кастомный код, который дергает микросервис и падает. До этого код спокойно дергал такой же микросервис на C#, почему же с Python появилась ошибка?

Оказалось, что в контракте есть ручка, которая в числе прочих полей и в С#, и в Python принимает на вход ID типа int. А iOS-приложение отправляет не сам integer, а строчку с integer внутри. C# из-за специфики сериализации/десериализации просто конвертирует эту строку в integer и пропускает дальше. В Python у нас с этим строже, поэтому и возникала ошибка. А поскольку этот клиент не был сгенерирован, разницу контрактов на проде мы не увидели.

Что помогло решить проблему быстро? Мой опыт работы с описанными выше скилами. Я просто склонировал iOS-приложение, сказал, где ошибка, и попросил разобраться. А приложение большое, там много нашей бизнес-логики. И LLM быстро определила проблему: вот здесь C# конвертирует, а Python нет.

Улучшайте код, а не промпты

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

LLM хорошо умеет вычленять правила написания из самого кода. Если в референсе везде есть docstring, то и в новом коде они появятся с высокой вероятностью. Если рядом есть код, который определенным образом оформляет инструкции, она сделает так же. Вообще, по моему опыту, если LLM делает что-то не так, то, скорее всего, где-то рядом есть код, в котором это так же плохо написано. Так что советую больше инвестировать не в простыню инструкций для LLM, а в улучшение вашей текущей кодовой базы, из которой AI сам почерпнет лучшие практики.