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

  • Здесь на помощь приходит ИИ. Его можно использовать не только как «генератор кода», а как инструмент для восстановления архитектурных идей старых систем. Это здорово экономит время на предварительном проектировании и снижает общую трудоёмкость.

  • Большинство коммерческих AI-сервисов сегодня отлично справляются с «случайной сложностью» разработки: генерация кода уже почти рутина и во многом автоматизирована. Но настоящий профит в модернизации — это работа с концептуальным уровнем.

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

  • А ещё LLM можно использовать для суммаризации: прогоняете через неё легаси-код — на выходе получаете более-менее внятный список бизнес-требований.

Фред Брукс в своей знаменитой работе “No Silver Bullet” («Нет серебряной пули») ещё в 80-х сформулировал неприятную мысль: кратного роста продуктивности разработки не будет, пока мы не научимся справляться с существенной сложностью (essential complexity). Под ней он понимал не сам процесс написания кода, а проработку взаимосвязанных компонентов системы. Код — это уже, по сути, «тривиальный» финальный штрих.

Сегодня у нас есть Copilot, aider и cline и куча других AI-ассистентов, которые без проблем превращают текстовое описание в рабочий код. Но если вспомнить Брукса, получается, что они закрывают в основном зону «случайной сложности»: помогают с рутиной, но почти не трогают то, что реально больно — спецификацию, проектирование и тестирование концептуальной конструкции.

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

Модернизация легаси-систем и проблема восстановления концепций

Если где-то и видно, насколько тяжело даётся концептуализация ПО, то это в легаси-системах. Они продолжают жить в проде, часто критичны для бизнеса, но при этом давно устарели и знакомы всё меньшему числу инженеров.

Главная их беда — «концептуальный дрейф»: когда бизнес развивается в одну сторону, а софт остаётся в другой. С каждым изменением вмешиваться в систему становится сложнее, и компании приходится либо мириться с расхождениями, либо как-то закрывать их костылями в процессах. В какой-то момент цена таких решений превращается в бизнес-риск, и тогда приходит время модернизации.

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

Представим e-commerce компанию, которая сначала работает только с розницей. Потом появляется отдельное подразделение для оптовиков — со своими правилами скидок и выставления счетов. Звучит логично, но вот где возникает проблема: если система для опта напрямую завязана на розничную реализацию, то вся «независимость» двух бизнес-единиц остаётся лишь на бумаге. По сути, функции оптового направления будут работать через правила, придуманные для розницы — и это классический пример концептуального дрейфа.

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

Методы применения ИИ для модернизации ПО

В разработке ПО цель всегда одна и та же — сократить цикл поставки, не превратив продукт в сборник багов. Мы попробовали применить ИИ на разных этапах модернизации и сфокусировались на том, что обычно выпадает из поля зрения коммерческих инструментов — проектирование.

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

Методы на этапе проектирования

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

Мы решили проверить, как LLM могут помочь в восстановлении концепций — и быстро уперлись в три серьёзные проблемы:

  1. Какой контекст вообще нужен и где его взять?

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

  3. Как обеспечить итеративное улучшение документов с требованиями?

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

Трассировка кода для получения контекста для LLM

Что такое трассировка?

Чтобы понять, как собрать полезный контекст кода для задач модернизации, мы посмотрели, как инженеры обычно изучают незнакомые кодовые базы с помощью современных IDE и статических анализаторов. Всё достаточно приземлённо: поиск по ключевым словам и регуляркам, навигация по соглашениям об именовании, диаграммы для визуализации структуры, плюс стандартные инструменты редактора — «go-to-definition» и «find-all-references», чтобы быстро прыгать по связям.

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

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

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

Простой пример: у нас приложение с формами. Стратегия может быть такой — «заморозить» слой форм и базу данных, а переписать только бизнес-логику между ними. Тогда полезная трассировка стартует с класса формы и идёт до узлов, где есть доступ к БД. Чтобы не утонуть в коде, глубину обхода ограничиваем.

Мы попробовали разные инструменты для парсинга и трассировки. Компиляторные тулчейны дают API под конкретные языки, а такие штуки, как tree-sitter или ANTLR, позволяют парсить кучу языков через единый API. В итоге мы остановились на Roslyn: его API возвращает детальные сведения о типах для VB и C#.NET. Наш обходчик AST сохранял всё необходимое — тип текущего символа, его исходный код и связи с другими элементами.

Сбор контекста кода

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

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

  • Markdown-форматированный контекст — где имена методов и классов размечены заголовками уровня H3.

  • AST в ASCII-стиле — вложенная структура, напоминающая дерево директорий в консоли.

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

Сбор контекста БД

Во время обхода AST мы дополнительно отслеживали зависимости от базы данных. Для этого с помощью ANTLR анализировали код на наличие SQL-запросов. Если встречался SQL, вытаскивали имена таблиц и хранимых процедур и привязывали их к соответствующим узлам AST. После завершения трассировки полученный список сравнивали с дампом схемы базы — так формировался отдельный markdown-файл с контекстом БД, где были только те таблицы и процедуры, к которым реально обращался код. В формате это выглядело похоже на контекст кода: каждая таблица или процедура имела заголовок H3 и соответствующую схему или SQL.

Чем хороша трассировка

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

Сделать контекст полезным для людей и LLM

Визуализация трассировки

Архитекторы и инженеры часто просили визуализации по результатам трассировки — просто читать дерево кода и список таблиц тяжело, гораздо удобнее видеть картинку. Поэтому мы сделали экспорт трейсов в PlantUML: генерировали диаграммы последовательностей и ER-диаграммы прямо из собранного контекста.

Мы пробовали генерировать PlantUML-код напрямую через LLM, но столкнулись с проблемами. Даже при относительно скромных контекстах (~50k токенов) модели теряли детали и путались в синтаксисе. В итоге получались диаграммы с ошибками или неполными связями.

Интересное наблюдение: сам PlantUML-код оказался ценным контекстом для LLM. Разметка диаграммы классов явно задаёт связи, и модель уже не нужно «догадываться», как именно соотносятся разные фрагменты кода. То же самое с ER-диаграммой — она даёт компактную сводку по слою хранения, от которого зависит приложение, и делает ответы модели заметно более технически точными.

Поэтому мы добавили PlantUML-разметку (классовые и ER-диаграммы) прямо в кодовый и базовый контекст, который передаём в LLM.

Восстановление бизнес-требований

Чтобы решить центральную проблему восстановления концепций, мы просили LLM составить документ с бизнес-требованиями (BRD), используя собранный контекст кода и базы данных. Следуя лучшим практикам промпт-инженеринга, мы разработали универсальный промпт, полезный как для технических, так и для нетехнических пользователей. Результаты включали общий обзор, функциональные требования, описание пользовательского интерфейса и список ссылок. Ниже приведено их краткое содержание.

Компонент вывода

Значимость

Общий обзор

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

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

Описание бизнес-правил, вычислений и обработки данных.

Пользовательский интерфейс

Описание элементов интерфейса, которые задействованы, и их места в пользовательском пути.

Ссылки

Перечень классов, таблиц и хранимых процедур, которые участвуют.

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

Например, в одних случаях задача — полностью «пересобрать» функциональность старой системы в новой среде. Здесь ключевой артефакт — это набор функциональных тест-кейсов, чтобы убедиться, что ничего не потерялось. В других проектах цель — наоборот, изменить поведение системы или добавить новую функциональность. Там уже важнее BRD (Business Requirements Document), который помогает разложить варианты движения к целевому состоянию, а функциональные тесты уходят на второй план.

Отдельная проблема — размеры контекста. У нас они легко переваливали за 150k токенов, что больше лимита даже у топовых моделей. Чтобы обойти это ограничение, мы сделали стратегию итеративных подсказок:

  1. Сначала считали количество токенов в контексте.

  2. Разбивали его на части примерно по 50k токенов.

  3. Просили модель последовательно суммировать каждую часть.

Для подсчёта использовали библиотеку tiktoken, но на практике работает и простое приближение: «1 токен ≈ 4 символа». Критически важно было не порезать код или SQL-запросы внутри — для этого мы использовали markdown-разделители, чтобы модель понимала границы фрагментов.

После того как все части контекста были обработаны, мы собирали их в единый ответ с помощью специального «сводного» промпта. В нём задавались пошаговые рассуждения, чек-лист обязательных разделов и просьба проверить полноту каждого блока. Если у вас есть возможность делать единый запрос со всем контекстом сразу — начинайте именно так. А с выходом моделей с расширенным контекстом (например, Gemini Flash или Llama 4) и ростом их «умения рассуждать» есть шанс, что в будущем вся эта история с итеративными подсказками просто не понадобится.

AI-чат о коде: работа с уточняющими запросами

В реальных проектах мы заметили, что первоначальный BRD почти всегда рождает уточняющие вопросы. Причём сами ответы на них зачастую оказываются не менее ценными, чем исходный документ — они дополняют или даже меняют его.

Мы пробовали подключать RAG (retrieval-augmented generation), но быстро столкнулись с проблемой: чтобы релевантность поиска была высокой, его приходилось долго и кропотливо настраивать. А запросы у пользователей слишком разные. Инженерам и архитекторам нужны были прямые ссылки на код, бизнес-аналитикам и продакт-менеджерам — смысловое описание. Сделать универсальное решение оказалось почти нереально, поэтому мы сосредоточились на нескольких самых популярных задачах и оптимизировали процесс именно под них.

Задача 1: объединение двух BRD

Когда нужно было слить два BRD (например, добавить новые результаты трассировки в уже готовый документ), мы использовали ту же стратегию итеративных подсказок для работы с большими контекстами. На этапе синтеза модель просто дополняла исходный BRD деталями из новой трассы.

Минус подхода — цена: она растёт линейно с объёмом кода и данных из БД, собранных трассировкой. Плюс накладывается стоимость самого шага синтеза. Но зато такой метод позволяет модели сосредоточиться на каждом фрагменте и максимально сохранить нюансы из обеих трасс, а это для архитекторов важнее экономии токенов.

Задача 2: поиск дополнительного релевантного контекста

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

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

Задача 3: Точечные запросы по текущей трассировке

Для точечных запросов к коду мы тестировали два подхода: итеративные запросы ко всему контексту (см. задачу 1) и индексацию во векторной базе (см. задачу 2). Итеративный вариант давал более полные и глубокие ответы, но стоил дороже. Поиск через векторку работал быстрее и дешевле, но терял часть деталей. Мы верим, что по мере появления моделей с действительно длинным контекстом практика «скармливать всё одним промптом» станет куда удобнее и выгоднее.

Заключение

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

Бонусом это ещё и расширяет команду: подключать к проектам можно специалистов с разным бэкграундом, потому что вся картина системы становится прозрачнее. Но в итоге всё упирается в одно: пока команда не разберётся в концепциях, на которых держится легаси, никакая модернизация не будет по-настоящему успешной. А вот с ИИ этот барьер преодолеть заметно проще.


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

Освоить эффективные методики управления проектами в IT можно на курсе «Руководитель IT проектов».

Комментарии (1)


  1. diakin
    02.09.2025 18:59

    превращают текстовое описание в рабочий код.

    А надо наоборот - легаси код в текстовое описание. "Что же разработчик хотел сказать своим кодом?" ))
    Вот еще на бинарник ИИ натравить, чтобы он превратил его "в текстовое описание" )