Автоматический рефакторинг Java Feature Flags с LLM: гайд по миграции на Unleash | AiManual
AiManual Logo Ai / Manual.
20 Мар 2026 Гайд

Рефакторинг кода на Java с помощью LLM: практический гайд по миграции Feature Flags

Пошаговая инструкция по использованию LLM для автоматической миграции Feature Flags в Java-коде. Готовые промпты, примеры кода до и после, актуальные инструмент

Взрываем легаси: почему миграция Feature Flags — это ад

Ты открываешь старый сервис, написанный на Java, и видишь это. Повсюду if (FeatureFlags.isEnabled("NEW_CHECKOUT")). Флаги живут в статичных методах, конфиг-файлах, а некоторые — в памяти у тимлида, который уволился год назад. Миграция на нормальную систему вроде Unleash кажется квестом на тысячу строк кода. Ручная правка — гарантия ошибки и неделя потраченного времени.

В 2026 году так делать — преступление против собственной продуктивности. Современные LLM (даже локальные, вроде Qwen2.5-Coder-32B или DeepSeek-Coder-V2) отлично справляются с семантическим анализом кода. Они не просто ищут строки — они понимают контекст. И этот контекст можно использовать для массового, но точного рефакторинга.

💡
Ключевая мысль: ты не используешь LLM как "умный grep". Ты ставишь ей задачу на уровне архитектуры: "Замени эту шаткую систему флагов на клиент Unleash, сохранив всю логику включения/выключения". Модель справится с вариациями, которые ты бы упустил.

От хака к стратегии: как LLM видит твой код

Простого поиска и замены по регуляркам недостаточно. Старый код — это зоопарк подходов:

  • Статические утилитные классы: LegacyFeatureToggle.check("flag")
  • Прямые чтения из properties: config.getBoolean("features.awesome")
  • Самопальные аннотации: @Feature("beta")
  • Флаги, захардкоженные в логике ветвления (да, такое еще встречается).

LLM, обученная на коде (а в 2026 году таких большинство), видит не просто строки, а паттерны. Она может сопоставить, что вызов isEnabled("flag") в классе OrderService и вызов check("flag") в PaymentProcessor — это одно и то же концептуально. Это и есть семантический рефакторинг. Если ты сталкивался с проблемой деградации контекста при длинных промптах, этот гайд покажет, как ее обойти.

1 Готовим поле боя: инструменты и анализ

Не бросайся сразу писать промпт. Сначала сделай инвентаризацию.

  1. Выбери цель миграции. Допустим, это Unleash версии 5.x (актуальной на 2026 год). Установи SDK в проект.
  2. Просканируй код. Используй простой скрипт, чтобы найти все упоминания флагов. Тебе нужен не просто список, а контекст — в каких методах, с какими параметрами они вызываются.
    # Быстрый поиск по проекту для оценки масштаба
    grep -r "isEnabled\|checkFeature\|FeatureFlags" --include="*.java" ./src
  3. Определи шаблоны. Сгруппируй находки. "Прямые вызовы статического метода", "чтение из конфигурации", "использование в условиях аннотаций". Это поможет создавать targeted промпты.

Предупреждение: Не пытайся мигрировать всё за один промпт. LLM, особенно локальная, может "перегреться" и начать галлюцинировать на больших объемах контекста. Разбей код на логические модули (пакеты, сервисы) и работай с ними по очереди. О том, как избежать галлюцинаций, я подробно писал в статье про языки для машин.

2 Пишем промпт-киллер: не проси, а командуй

Плохой промпт: "Замени флаги на Unleash". Хороший промпт — это техническое задание для инженера.

// Пример КОДА ДО рефакторинга (старая система):
public class OrderService {
    private Config config;

    public BigDecimal calculateDiscount(Order order) {
        if (FeatureFlags.isEnabled("DISCOUNT_V2", order.getUserId())) {
            return newV2DiscountLogic(order);
        }
        if (config.getBoolean("features.legacy_discount_enabled")) {
            return legacyDiscount(order);
        }
        return BigDecimal.ZERO;
    }
}

Теперь сам промпт. Он должен быть конкретным, императивным и содержать пример вывода.

Ты выполняешь рефакторинг Java-кода для миграции системы feature flags на Unleash 5.x.

КОНТЕКСТ ПРОЕКТА:
- Уже добавлена зависимость: `io.getunleash:unleash-client-java:5.2.0`
- Внедрен бин `Unleash` (Spring Bean или аналогичный), доступный через `unleash`.
- Старая система использовала класс `FeatureFlags` со статическими методами.
- Также использовалось прямое чтение из `Config` (интерфейс, метод `getBoolean`).

ЗАДАЧА:
Проанализируй предоставленный Java-код. Найди ВСЕ использования старой системы feature flags и замени их на вызовы `unleash.isEnabled("FEATURE_NAME")`.

ПРАВИЛА ЗАМЕНЫ:
1. `FeatureFlags.isEnabled("FLAG_NAME", userId)` -> `unleash.isEnabled("FLAG_NAME", new UnleashContext.Builder().userId(userId).build())`
2. `FeatureFlags.isEnabled("FLAG_NAME")` -> `unleash.isEnabled("FLAG_NAME")`
3. `config.getBoolean("features.flag_name")` -> `unleash.isEnabled("FLAG_NAME")` (преобразуй kebab-case в FLAG_NAME).
4. Локальные переменные и параметры методов должны быть сохранены.
5. Импорты старого класса `FeatureFlags` должны быть удалены.

ВХОДНЫЙ КОД (который нужно отрефакторить):
```java
[Сюда будет вставлен реальный фрагмент кода]
```

ВЫВЕДИ ТОЛЬКО ОТРЕФАКТОРЕННЫЙ КОД, без пояснений. Сохрани исходное форматирование.

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

3 Исполнение и валидация: доверяй, но проверяй

Запускаешь LLM (Claude 3.7 Sonnet, GPT-4o 2026, или локальную DeepSeek-Coder через llama.cpp). Получаешь результат.

// Пример КОДА ПОСЛЕ рефакторинга:
public class OrderService {
    private Unleash unleash; // Внедрено через конструктор
    // private Config config; // Удалено, если больше не используется

    public BigDecimal calculateDiscount(Order order) {
        if (unleash.isEnabled("DISCOUNT_V2", UnleashContext.builder().userId(order.getUserId()).build())) {
            return newV2DiscountLogic(order);
        }
        if (unleash.isEnabled("LEGACY_DISCOUNT_ENABLED")) { // Имя флага преобразовано
            return legacyDiscount(order);
        }
        return BigDecimal.ZERO;
    }
}

И вот тут начинается настоящая работа. LLM — не волшебник.

  • Запусти компиляцию. Первое же javac покажет, не удалила ли модель нужный импорт или не сломала ли сигнатуру метода.
  • Пиши юнит-тесты. Сейчас. Не после миграции всего проекта, а сразу для отрефакторенного модуля. Тесты — это твой автоматический чекер против семантических ошибок (флаг перепутан, логика инвертирована).
  • Используй статический анализ. Инструменты вроде Checkstyle или SpotBugs могут найти проблемы, которые ускользнули от LLM (например, потенциальный NPE после удаления старой переменной `config`).

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

Где споткнешься: подводные камни автоматического рефакторинга

Ошибка Почему происходит Как предотвратить
Потеря контекста пользователя LLM видит `isEnabled("flag")`, но не замечает, что выше по методу есть `userId`, который нужно передать в `UnleashContext`. Явно укажи в промпте правила для методов с дополнительными параметрами. Разбивай большие методы на части перед анализом.
Некорректное именование флагов Преобразование `"features.legacy_discount"` в `"LEGACY_DISCOUNT"` может не соответствовать реальным ключам в Unleash. Создай и предоставь LLM mapping-таблицу старых ключей на новые в рамках промпта.
"Слепые" зоны в коде Флаги, зашитые в строки, которые собираются динамически (`"FEATURE_" + stage`). LLM их не распознает как флаги. Для такого кода автоматизация не подходит. Выявляй такие случаи на этапе анализа и обрабатывай вручную.

Финальный акт: интеграция в пайплайн

Разовые победы — это круто, но устойчивый процесс — это профессионализм. Настрой автоматический пайплайн для проверки рефакторинга.

  1. Создай скрипт-обертку, который берет кусок кода, применяет промпт через LLM API (OpenAI, Anthropic, или локальный сервер с Ollama) и сохраняет результат.
  2. Интегрируй в pre-commit хуки. Перед коммитом изменений, запускай быстрые тесты на отрефакторенных файлах.
  3. Используй дифференциальный подход. Мигрируй флаги не во всем проекте сразу, а по функциональным блокам. После миграции каждого блока — пулл-реквест, ревью, мерж. Это снижает риск.

И главное — не становись заложником инструмента. Если видишь, что LLM стабильно ошибается в определенном паттерне, не исправляй вручную сто раз. Обнови промпт. Добавь новое правило или пример. Твой промпт — это живой документ, который должен становиться умнее с каждой итерацией. Это тот самый эволюционный процесс оптимизации кода.

К 2026 году ручной рефакторинг устаревших фич-флагов должен вызывать такую же реакцию, как правка продовой базы данных через консоль в пятницу вечером — легкую панику и вопрос "зачем?". Автоматизируй рутину, но делай это с холодной головой и четким планом. И тогда освободившееся время можно потратить на то, что ИИ пока не умеет — на архитектурные решения, которые не придется рефакторить через три года.

Подписаться на канал