AI для анализа устаревших систем: восстановление требований и документации из кода
Вам знакомо это чувство: открываешь репозиторий легаси-проекта, а там — ноль документации. Совсем. Ни одного требования, ни архитектурной диаграммы, ни пояснения, почему модуль оплаты написан так, а не иначе. Только код, комментарии в стиле «пофиксил баг» и человек, который это писал три года назад и уже уволился. Дважды.
Ручной реверс-инжиниринг такого проекта — это недели чтения кода, попыток понять, где заканчивается один ограниченный контекст и начинается другой, и бесконечные вопросы к команде «а это зачем?». На каждый ответ получаешь пожимание плечами — никто уже не помнит.
LLM меняет расклад. Не в том смысле, что модель сама всё разберёт и выдаст вам папку с готовой документацией (это было бы магией, а её не существует). Но она может превратить механическую рутину по разбору кодовой базы в управляемый процесс с конкретными артефактами на выходе: требования, диаграммы, ADR. Давайте разберём, как это работает на практике.
Почему писать документацию постфактум руками — провальная стратегия
Если вам «повезло» разбирать легаси, вы знаете этот сценарий. Садитесь читать код. Находите контроллер на 800 строк. Внутри — вызов сервиса, который вызывает ещё три сервиса, а те дёргают внешнее API, пишут в БД и попутно отправляют событие в Kafka. Через два часа вы понимаете, что прочитали 3% кодовой базы и всё ещё не знаете, какие бизнес-правила реализованы в этом модуле.
Проблема не в том, что вы плохо читаете код. Проблема в масштабе: легаси-система из 50 тысяч строк содержит сотни неявных архитектурных решений. Их извлечение вручную — работа на месяцы. И — что немаловажно — работа, которую никто не будет делать, потому что «надо фичи пилить».
А теперь посмотрите на это с другой стороны. LLM читает 50 тысяч строк за секунды. Она не устаёт, не отвлекается и не пропускает 600-строчные контроллеры по диагонали. Её проблема — не в скорости чтения, а в том, чтобы правильно сформулировать задачу: что именно искать в коде и в каком формате выдать.
Pipeline восстановления: от легаси-кода к документации
Весь процесс укладывается в 7 этапов. Сначала — общая схема, потом каждый этап с подробностями.
Диаграмма показывает сквозной конвейер: от серой «точки входа» — устаревшей системы без документации — код извлекается (синий), парсится LLM на структурные блоки (жёлтый) и расходится на три параллельных потока анализа: паттерны, бизнес-правила и карты зависимостей (все фиолетовые). Паттерны и правила сходятся в требованиях (синий), зависимости — в диаграммах (синий). Оба потока объединяются в ADR-черновиках (зелёный), которые уходят на валидацию аналитику (жёлтый). Пунктирная связь от валидации обратно к требованиям — это нормально: с первого захода идеально не бывает, итерации неизбежны.
Примечательно, что на каждом этапе LLM выполняет разную роль. На парсинге она — статический анализатор. На поиске паттернов — архитектор, узнающий знакомые структуры. На генерации ADR — технический писатель. И это не три разных модели — это одна и та же, просто с разными промптами. Собственно, к промптам и перейдём.
Этап 1: Инвентаризация — что вообще есть в репозитории
Первое, что нужно сделать — понять масштаб бедствия. Сколько модулей, какие технологии, какие внешние зависимости. Без этого вы рискуете пропустить кусок системы, который живёт в отдельном репозитории, но критичен для бизнес-логики.
Промпт для инвентаризации:
Ты — системный аналитик, который впервые видит этот проект.Проанализируй структуру репозитория и выдай:
1. Технологический стек: язык(и), фреймворки, БД, брокеры сообщений, серверы кеширования, API Gateway.2. Список модулей/пакетов верхнего уровня с кратким описанием назначения каждого (одно предложение).3. Внешние интеграции: какие сторонние API или системы вызывает этот проект? Найди их по импортам HTTP-клиентов, gRPC-стабам, конфигурационным файлам.4. Схема БД: перечисли таблицы и ключевые поля. Если в коде нет миграций, ищи ORM-модели (Entity, @Table, Sequelize.define).5. Точки входа: HTTP-контроллеры, gRPC-сервисы, консьюмеры очередей.
Для каждого пункта укажи файл-источник (путь от корня репозитория)и номер строки, где ты это нашёл. Если что-то не найдено — напиши«[НЕ НАЙДЕНО]», не выдумывай.
Ниже — содержимое репозитория. Для экономии контекста я передаютолько структуру файлов и ключевые фрагменты. Если нужно большеконтекста — скажи, какой файл загрузить следующим.
[структура репозитория или фрагменты кода]После этого промпта у вас на руках карта проекта. Не детальная, но достаточная, чтобы понять, с чем имеем дело. Если на шаге 5 модель пишет «[НЕ НАЙДЕНО]» напротив консьюмеров очередей — значит, либо их действительно нет, либо они в другом репозитории. Это уже знание, а не догадка.
Этап 2: Карта зависимостей
На этом этапе нам нужно понять, кто кого вызывает. Без этого все последующие диаграммы будут гаданием. Промпт:
На основе кода ниже построй карту зависимостей между компонентами.
Для каждого компонента (сервис, контроллер, обработчик) укажи:1. Имя компонента.2. Что он вызывает: список зависимостей с типом взаимодействия (HTTP-запрос, вызов метода, запись в БД, публикация события, чтение из очереди).3. Кто его вызывает: список компонентов, которые используют этот.
Формат вывода — иерархический список:Модуль оплаты (PaymentModule): → PaymentGatewayClient (HTTP POST /api/v1/charge) → OrderRepository (запись в таблицу payments) → EventPublisher (событие payment.completed)
Не включай зависимости на уровне утилит и хелперов(Logger, DateTimeFormatter). Только бизнес-компоненты.
[фрагменты кода модуля]Этап 3: Извлечение бизнес-правил
Самая ценная часть. Бизнес-правила — это то, ради чего система вообще существует, и именно их никто никогда не документирует. Они растворены в условных операторах, валидациях, конечных автоматах и магических константах.
Проанализируй код и извлеки все бизнес-правила. Бизнес-правило —это логика, которая определяет, ЧТО можно делать, КОГДА иПРИ КАКИХ УСЛОВИЯХ. Это НЕ техническая логика (не пулысоединений, не ретраи, не форматирование дат).
Для каждого правила укажи:
1. Идентификатор (BR-001, BR-002, ...).2. Формулировка на человеческом языке: «Если ..., то ...». Например: «Если сумма заказа превышает 100 000 рублей, то требуется подтверждение менеджера».3. Источник в коде: файл, строка, фрагмент кода.4. Тип: Валидация / Вычисление / Процесс (workflow) / Ограничение доступа / Интеграционное.5. Связанные сущности: какие доменные объекты затронуты.6. Признак: Явное (прямо выражено в коде) / Неявное (выведено из структуры кода).
Отдельно отметь правила, которые кажутся противоречащими другдругу (например, BR-005 разрешает скидку, а BR-012 запрещает еёдля той же категории пользователей).
[фрагменты кода модуля]Здесь есть тонкость: LLM склонна принимать любой if за бизнес-правило. if (user == null) throw new AuthException() — это не бизнес-правило, это защитное программирование. Поэтому в промпте явно сказано «это НЕ техническая логика». Но даже с этим уточнением часть правил придётся вычищать вручную — модель перестраховывается и включает всё подряд.
Стоит отметить: примерно 30–40% извлечённых таким образом правил потребуют уточнения у команды. Не потому что LLM ошиблась, а потому что код отражает не намерение, а реализацию. Реализация могла быть компромиссом: «сделаем пока так, потом перепишем». «Потом» не наступило (классика), а компромисс выглядит как бизнес-правило.
Этап 4: Восстановление функциональных и нефункциональных требований
Теперь самое интересное: превращаем паттерны кода и бизнес-правила в требования. Тут работаем в два прохода — ФТ и НФТ.
Функциональные требования:
На основе бизнес-правил и структуры кода (ниже) восстановифункциональные требования, которые были реализованы.
Для каждого ФТ:1. Идентификатор (FR-001, FR-002, ...).2. Название (до 10 слов).3. Описание: что система делает.4. Actor: кто инициирует (роль пользователя или система).5. Предусловие.6. Основной поток.7. Альтернативные потоки.8. Связь с бизнес-правилами: укажи BR-xxx.9. Степень уверенности: Высокая (явно из кода) / Средняя (из комбинации правил) / Низкая (предположение).
Требования с низкой уверенностью пометь как«[ТРЕБУЕТ ПОДТВЕРЖДЕНИЯ]». Не выдумывай требования,которых нет в коде.
Бизнес-правила и структура кода:[результаты этапов 2 и 3]Нефункциональные требования:
На основе кода восстанови нефункциональные требования.Ищи их в:
1. Конфигурациях (connection pool size, thread pool, cache TTL).2. Аннотациях и декораторах (@Transactional, @Retryable, @Timed, @Cacheable).3. Константах (MAX_RETRIES, TIMEOUT_MS, RATE_LIMIT).4. Комментариях к полям БД (VARCHAR(255) с комментарием «ограничено требованиями PCI DSS»).5. Библиотеках (Hystrix → требование к отказоустойчивости, Redis → кеширование).
Для каждого НФТ:1. Идентификатор (NFR-001, NFR-002, ...).2. Категория: Производительность / Безопасность / Надёжность / Масштабируемость / Удобство использования / Совместимость.3. Описание.4. Метрика (если найдена в коде).5. Источник в коде (файл, строка).6. Признак: Явное (конфиг) / Косвенное (выведено из библиотеки).
[фрагменты кода с конфигурациями, аннотациями, константами]Самый ценный источник НФТ в легаси — это конфигурационные файлы и аннотации. application.yml с connection-timeout: 5000 и pool.size: 20 — это требование к производительности, просто никто не потрудился его сформулировать словами. LLM делает именно это: переводит конфиги обратно в человеческий язык.
Этап 5: Генерация диаграмм
После того как требования восстановлены, нужны диаграммы. Не чтобы «красиво было» (хотя красиво — тоже неплохо), а чтобы команда могла быстро понять, как система устроена. Без диаграмм вы получаете документ на 40 страниц, который никто не прочитает.
C4-диаграммы для легаси особенно хороши тем, что они показывают не только компоненты, но и связи между ними — то, что в коде размазано по десяткам файлов. Если практика C4 для вас новая — посмотрите AI в проектировании архитектуры: LLM для C4-диаграмм, ADR и trade-off анализа, там разбор подхода с примерами промптов.
Промпт для генерации C4 Container диаграммы:
Ты — архитектор. На основе карты зависимостей (ниже) постройC4 Container диаграмму в формате Mermaid (flowchart TD).
Правила:- Каждый контейнер — отдельный узел с квадратными скобками.- Связи — стрелки с подписями: тип протокола и назначение.- Цвета: синий для внутренних сервисов, зелёный для БД, фиолетовый для шины сообщений, серый для внешних систем.- Каждый узел должен содержать краткое описание (1 строка).- Используй style для раскраски (например, style PaymentService fill:#4a90d9,stroke:#2c5f8a,color:#fff).
Карта зависимостей:[результаты этапа 2]Модель выдаст Mermaid-код. Его можно сразу рендерить — или вставить в Confluence, он там поддерживается нативно.
Этап 6: Черновики ADR
ADR — это не только про новые решения. Часто архитектурно значимые решения уже приняты и живут в коде, просто не задокументированы. LLM может восстановить их постфактум — это не полноценный ADR, а скорее черновик, который команда допилит.
Промпт для генерации ADR-черновика:
Ты — архитектор, восстанавливающий архитектурные решенияиз легаси-кода. Найди в коде признаки архитектурных решенийи оформи их как ADR по шаблону:
## ADR-NNN: <Название решения>
**Статус:** Accepted (выведено из кода)**Дата:** неизвестна (нет в git history)**Контекст:** в какой ситуации принималось решение.**Решение:** что было выбрано и как реализовано.**Альтернативы:** какие ещё варианты были возможны на основе паттернов в коде.**Последствия:** что из этого решения следует сейчас (из кода: зависимости, ограничения).
ADR, которые стоит искать:- Единая точка входа для платежей (PaymentGateway).- Использование событийной модели вместо прямых вызовов.- CQRS-подход (разделение чтения и записи).- Выбор конкретной БД под задачу (из конфигов и ORM-моделей).
[фрагменты кода, результаты этапов 2-5]Почему важно генерировать именно черновики ADR, а не просто раздел «Архитектурные решения» в описании системы? Потому что ADR — это формальный документ с контекстом и последствиями. Он даёт ответ не только на вопрос «как сделано», но и «почему сделано именно так». Даже если исходные причины принятия решения утеряны (автор уволился), зафиксированный факт «в коде это выглядит так, вероятная причина — X» уже лучше, чем ничего. Подробнее о практике ADR — в ADR: Architecture Decision Records — зачем и как документировать архитектурные решения.
Как это выглядит в бою: кейс с модулем оплаты
Давайте посмотрим на весь процесс на конкретном примере — модуль оплаты легаси-монолита интернет-магазина. Код на Java/Spring, 12 тысяч строк, 3 года без документации, первоначальный разработчик в отпуске. Навсегда.
Диаграмма показывает итеративный процесс: аналитик ставит задачу, LLM сканирует код модуля оплаты и выдаёт первый драфт требований. Аналитик задаёт уточняющий вопрос («откуда взято требование про 3 попытки?»), модель находит константу MAX_RETRIES = 3 в PaymentService.java:142 — и требование становится верифицированным, а не предположительным. Затем запрос на C4-диаграмму — и модель анализирует импорты и HTTP-клиенты, чтобы построить контейнерную схему с реальными связями между сервисами. На выходе — полный пакет: требования с трассируемостью до кода, диаграммы и черновики ADR.
Что получилось в сухом остатке:
- 12 функциональных требований — от «применить промокод» до «обработать частичный возврат средств», каждое с привязкой к файлу и строке.
- 5 нефункциональных требований — включая «таймаут платёжного шлюза ≤ 30 секунд» (из
application.yml) и «повторные попытки при ошибке: 3 раза с экспоненциальной задержкой» (из@RetryableсmaxAttempts=3). - C4 Container диаграмма с шестью компонентами и реальными связями.
- 3 ADR-черновика — включая решение использовать внешний платёжный шлюз как единую точку входа для всех платежей.
- 2 конфликта — например, требование «скидка не применяется к товарам со статусом “распродажа”» противоречило логике в другом сервисе, которая проверяла только цену, но не статус товара.
Время: 35 минут работы аналитика (10 — настройка промптов, 15 — прогон кода через LLM, 10 — валидация и уточнения). Без LLM ручной разбор 12 тысяч строк занял бы 3–4 дня. Это не silver bullet — половину сгенерированных требований всё равно пришлось уточнять у команды. Но теперь у команды есть что уточнять, а не просто белый лист с заголовком «Требования к модулю оплаты».
Что LLM делает плохо (и зачем всё равно нужен аналитик)
Вот где модель спотыкается — и это важно понимать, чтобы не попасть в ловушку ложного ощущения завершённости.
Давность кода. LLM не различает живой код и мёртвый. Если в репозитории лежит OldPaymentService.java с аннотацией @Deprecated — модель честно извлечёт из него требования. Вам придётся вручную отсеивать устаревшие модули. Это не баг, но про это забывают.
Код, оставленный «на будущее». Комментарий // TODO: implement fraud detection — не требование. Но LLM, увидев пустой метод с многообещающим названием, может решить, что это часть системы. Фильтруйте TODO, FIXME и заглушки.
Архитектурные дрейфы. Когда система развивается 5 лет, в ней накапливаются пережитки: устаревший способ вызова, который уже никто не использует, но удалить страшно. LLM не понимает, что этот код — исторический артефакт. Она видит два способа сделать одно и то же и генерирует требования для обоих.
Бизнес-контекст за пределами кода. Требование «оплата должна приниматься в долларах» может быть в коде потому, что бизнес когда-то планировал выход на международный рынок — а может быть потому, что разработчик скопировал пример из Stack Overflow и не поменял валюту. Без доменного контекста разница неочевидна, и модель здесь бесполезна.
Таблица: типичные находки в легаси и что с ними делать
| Что модель нашла в коде | Что это значит на самом деле | Что делать аналитику |
|---|---|---|
@Retryable(maxAttempts=3, backoff=@Backoff(delay=2000)) | Требование к надёжности интеграции | Зафиксировать как NFR, уточнить у архитектора |
if (order.amount > 100_000) { requireApproval(); } | Бизнес-правило: лимит на автоматическое одобрение | Зафиксировать как BR/FR |
// TODO: implement caching | Незавершённая задача, не требование | Игнорировать |
@Deprecated на целом классе | Мёртвый код, который боятся удалить | Исключить из анализа |
sendToQueue("payment.events", event) | Событийная архитектура, асинхронная связь | Зафиксировать как архитектурное решение в ADR |
connection-timeout: 30000 в конфиге | НФТ: максимальное время ожидания ответа | Зафиксировать как NFR с метрикой |
| Два контроллера для одного эндпоинта | Архитектурный дрейф, один устарел | Сравнить с git blame, оставить актуальный |
VARCHAR(255) с комментарием PCI DSS | Регуляторное ограничение на хранение данных | NFR: безопасность, регуляторное требование |
Заключение
Легаси без документации — это не приговор. Это задача, в которой можно заменить три недели ручного чтения кода на час работы с LLM и день валидации результатов. Магии нет — модель не напишет за вас идеальную документацию с первой попытки. Но она прочитает 12 тысяч строк кода, найдёт все if, конфиги и аннотации и разложит их по шаблонам. А вы проверите, что из этого бизнес-логика, что — мёртвый код, а что — технический долг.
Попробуйте на одном модуле: скормите LLM код, получите драфт требований, диаграмму и ADR. Покажите команде. Если они скажут «ого, откуда это?» — вы на правильном пути. Если скажут «ерунда, всё не так» — не расстраивайтесь. Это значит, что документация в головах у команды, а вы только что нашли повод её оттуда вытащить.
P.S. Если у вас в проекте 50 тысяч строк легаси, а начальство говорит «неделя на реверс-инжиниринг» — не спорьте. Просто запустите LLM. Через день вы принесёте им 40 требований, 5 диаграмм и список архитектурных решений — и спросите, чем занять оставшиеся четыре дня.