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

Знакомая картина: модуль на 4000 строк, который «писал один человек и ушёл в 2019-м», трогать страшно, а бизнес просит новую фичу к пятнице. Рука тянется к ChatGPT: «перепиши этот класс нормально». В половине случаев это заканчивается зелёным PR, в другой половине — инцидентом в проде через три дня. Разница не в модели, а в том, задали ли вы себе два вопроса до промта: есть ли покрытие тестами и насколько модуль связан с остальной кодовой базой.

Ниже — два сценария работы с legacy через LLM, проверенные промты для каждого и критерий, по которому я выбираю между ними. Не «N промтов для категории», а инструменты под конкретное решение.

Миф первый: «LLM перепишет монолит целиком, и станет хорошо»

Это работает ровно в одном случае — когда модуль почти не связан с другими и у вас есть тесты, которые фиксируют его поведение. Если хоть одно условие не выполняется, полное переписывание по описанию — это лотерея с чужими деньгами. Подробнее — в статье «Юнит-тесты на Python с LLM: 12 промтов от pytest-фикстур до Hypothesis, которые закрывают непокрытые ветки».

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

Обратная сторона: постепенное «выщипывание» функций (вытащили одну ответственность, покрыли тестом, вынесли в отдельный модуль) — медленно. Модуль на 4000 строк так можно разбирать неделями.

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

Как измерить связанность до того, как трогать код

Не начинайте с рефакторинга. Начните с карты зависимостей — её LLM строит по коду достаточно надёжно, потому что это задача на извлечение, а не на догадку.

Промт:

Ты анализируешь legacy-модуль перед рефакторингом. Вот код модуля [вставь код].
Составь карту зависимостей строго по фактам из кода, без предположений:
1. Что модуль импортирует и какие из этих импортов реально используются (перечисли функции/классы).
2. Кто вызывает этот модуль: найди по именам экспортируемых функций/классов [перечисли имена] и укажи файлы и строки.
3. Какие структуры данных (DTO, схемы БД, JSON-поля) модуль разделяет с внешним миром — перечисли поля и где они читаются/пишутся снаружи.
4. Побочные эффекты: запись в БД, файлы, сеть, глобальное состояние, изменение аргументов на месте.
Для каждого пункта — ссылка на файл и строку. Если данных в переданном коде не хватает, напиши «не видно из кода», а не додумывай.

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

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

Миф второй: «тесты можно сгенерировать после рефакторинга»

Тесты, написанные после изменения кода, фиксируют новое поведение, включая случайно сломанное. Они зелёные, вы спокойны — а баг уже в проде. Тесты-«страховка» генерируются до правок и по исходному коду.

Промт для генерации страховочных тестов:

Вот legacy-функция [вставь код]. Сгенерируй тесты на pytest, которые фиксируют ТЕКУЩЕЕ поведение, включая странное.
Требования:
- Покрой: happy path, граничные значения, обработку None/пустых, исключения.
- Отдельно отметь места, где текущее поведение выглядит как баг (например, молчаливый возврат None вместо ошибки). Для них напиши тест, который фиксирует поведение КАК ЕСТЬ, с комментарием # legacy behavior, do not change without review.
- Не мокай то, что можно вызвать напрямую; моки — только для сети, БД и времени.
- Для каждой ветки кода укажи, какой тест её покрывает. Если ветку покрыть нельзя без рефакторинга — перечисли такие ветки отдельным списком.

Этот последний список — золото. Он показывает, какие части функции вообще нельзя проверить, не разбив её. Именно с них и начинается выщипывание.

Проверьте тесты на «мутацию» до рефакторинга Прежде чем доверять страховочным тестам, сломайте код намеренно: поменяйте условие, уберите проверку. Если тест остался зелёным — он ничего не страхует. Промт: «Вот функция и её тесты. Предложи 5 однострочных мутаций кода, которые должны ронять тесты. Для каждой скажи, какой тест её поймает, и отметь мутации, которые пройдут незамеченными».

Сценарий «выщипывание»: как просить LLM выделить ответственности

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

Промт для выделения ответственностей:

Разбей эту legacy-функцию/класс на ответственности, не меняя код.
Для каждой ответственности укажи:
1. Название (что она делает по существу, одной фразой).
2. Какие строки исходного кода к ней относятся.
3. Какие данные она читает и какие меняет (входы/выходы/побочные эффекты).
4. Связана ли она с другими ответственностями через общее состояние.
В конце предложи порядок выноса: какую ответственность выносить первой, чтобы разорвать минимум связей, и почему именно её.
Не пиши новый код — только анализ и порядок шагов.

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

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

Сценарий «переписывание»: как не потерять контракты

Если карта зависимостей показала изоляцию и тесты есть — можно переписывать. Но не «перепиши нормально», а с явным списком контрактов, которые нельзя менять. Подробнее — в статье «LeakCanary показал стек — а виноват static context: промты, которые заставляют LLM объяснить OOM в Android».

Промт для переписывания с фиксацией поведения:

Перепиши модуль [код] с нуля. Жёсткие ограничения:
- Публичный API (имена функций, аргументы, типы возврата) не меняется.
- Формат ошибок и коды не меняются.
- Побочные эффекты сохраняются: [перечисли из карты зависимостей].
- Внешние структуры данных не трогаются: [перечисли поля].
Для каждого ограничения покажи, где в новом коде оно соблюдено.
Затем перечисли места, где ты сознательно отошёл от старого кода, и объясни, почему это не ломает поведение. Если уверенности нет — не отходи, отметь как «требует решения человека».

Секция «где отошёл» — это ваш чек-лист для ревью. Модель честно показывает развилки, вместо того чтобы тихо принять решение за вас.

Миф третий: «зелёные тесты = рефакторинг не изменил поведение»

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

Проверочный промт после рефакторинга:

Вот старый код и новый код [оба блока]. Вот тесты, которые оба проходят.
Найди различия в поведении, которые тесты НЕ ловят:
1. Ветки, покрытые в одном варианте и не покрытые в другом.
2. Изменения в порядке побочных эффектов.
3. Изменения в обработке исключений и граничных значений.
4. Изменения в производительности, которые могут проявиться под нагрузкой (лишние запросы в цикле, N+1).
Для каждого различия — конкретный вход, на котором поведение разойдётся, и тест, который его поймает.

Этот промт — по сути, ручной дифференциальный анализ. Он не гарантирует отсутствие багов, но находит те дыры, которые не видит зелёный CI. По опыту, различия в порядке побочных эффектов и N+1 — самые частые «невидимые» поломки при переписывании.

Что выбрать: короткая таблица

Условие Выщипывание Переписывание
Покрытие тестами низкое или отсутствует есть страховочные тесты
Связанность с модулями высокая низкая
Сроки недели дни
Риск инцидента ниже, шаги обратимы выше, откат — целиком
Когда оправдано живой прод, много вызывающих изолированный модуль, ясные контракты

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

А когда рефакторинг дошёл до ревью PR — не пропускайте его через ту же модель «на глаз»: Промты для код-ревью: как заставить LLM находить баги до мержа.

С чего начать завтра

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

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

← Все статьи

Спросить AI по этой статье

Задайте вопрос по этой статье — ASI Biont ответит, опираясь на её текст.

Читайте также

Ораторское искусство и харизма: как перестать бояться вопросов после выступления (практический гайд)

27 октября 2026

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

27 октября 2026

Юнит-тесты на Python с LLM: 12 промтов от pytest-фикстур до Hypothesis, которые закрывают непокрытые ветки

27 октября 2026

Расторжение договора с фитнес-клубом или салоном красоты и возврат денег: разбор условий, сроков и претензий в 2026 году

27 октября 2026

Cambridge Primary English (0058): почему начальные классы решают, будет ли ребёнок когда-либо читать бегло — и как на самом деле работает программа

27 октября 2026

Full-Stack AI Engineer: как RAG-пайплайны, векторные базы данных и развертывание LLM на самом деле работают в продакшене

28 октября 2026

CIPM / CIPP/E — Управление конфиденциальностью и GDPR (IAPP): как статистика правоприменения GDPR превращает риск несоответствия требованиям в статью бюджета

28 октября 2026

Сверка актов вручную или промптом: где LLM экономит бухгалтеру часы, а где создаёт риски

12 ноября 2026

Продакт-менеджмент и рост: стратегия, метрики и решение, которое действительно меняет ситуацию

11 ноября 2026