Разработчик, покажи мне свой skill.md Рефакторинг легаси

от автора

Почему бы написать статью в виде одного только агентского скилла. И вам полезно и мне не трудно.

Писал паттерн для себя. И для агента.


type-driven-legacy-refactoring

Универсальная методология рефакторинга и проектирования долгоживущих корпоративных систем с богатой бизнес-логикой на основе предварительного выявления намерений, археологии домена, функционального DDD Скотта Влашина и стратегии тестирования Майкла Физерса.

Применимость:

  • При рефакторинге и развитии долгоживущих продуктов, которые уже живут годами и будут развиваться дальше, где цена архитектурной ошибки крайне высока.

  • В проектах с высокой доменной сложностью и богатой бизнес-логикой (многостадийные визарды, финансовые/миграционные заявки, скоринги, биллинг).

  • Когда необходима «археология домена» — извлечение утраченных или неявных бизнес-правил из процедурного легаси-кода для их явной фиксации в новой архитектуре.

  • При жестких требованиях к надежности, отказоустойчивости (защита от разрывов сессий и сети) и статическим гарантиям корректности данных (Valid by Construction).


Навык агента (Agent Skill): Type-Driven Architecture & Refactoring via Intent Discovery, Wlaschin DDD & Feathers Testing

Этот навык представляет собой универсальный инженерный протокол проектирования и безопасного рефакторинга систем. Его ключевое отличие — обязательная предварительная фаза выявления намерений, приоритетов и архитектурных противоречий, а также опора на синтез теорий Скотта Влашина и Майкла Физерса.


ГРАНИЦЫ ПРИМЕНИМОСТИ (Где методология дает максимальный ROI)

Данный инженерный протокол является мощным архитектурным инструментом и дает стократный возврат инвестиций именно в следующих условиях:

  1. Богатая бизнес-логика и высокая доменная сложность: Система содержит десятки взаимосвязанных правил, квот, ограничений и пошаговых переходов состояний.

  2. Долгоживущие продукты (Long-Lived Core Products): Продукт жил годами, накопил процедурный долг и будет развиваться и поддерживаться еще долгие годы.

  3. Необходимость «археологии домена»: Исходные требования или авторы утрачены, а реальные бизнес-правила и крайние случаи скрыты внутри тысяч строк легаси-скриптов и баз данных. Характеризационные тесты здесь выступают инструментом раскопки истины.

  4. Высокие требования к надежности и архитектурным свойствам: Любой сбой в продакшене (Fatal Error, потеря сессии при вводе) недопустим из-за финансовых, юридических или репутационных рисков.

(Примечание: Для простых одноразовых CRUD-прототипов или быстрых MVP этот подход будет избыточным оверинжинирингом).


0. Фаза 0: Выявление намерений, приоритетов и разрешение противоречий (Intent & Contradiction Discovery)

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

Протокол Фазы 0:

  1. Выявление намерений и приоритетов (Intents & Priorities):

    • Что важнее в данном контексте: абсолютная типобезопасность ядра, скорость клиентского UI-ввода, минимизация дублирования кода (DRY) или отказоустойчивость при сетевых сбоях?

    • Каковы ограничения среды (например: «у нас часто дохнет сессия», «обрывы сети при отправке виртуальных форм»)?

  2. Картирование противоречий (Contradiction Mapping):

    • Конфликт 1: Требование UX (собрать и показать на веб-форме все ошибки ввода без падения) противоречит принципу доменной строгости Valid by Construction (мгновенно выбросить исключение при первой ошибке).

    • Конфликт 2: Требование фонового автосохранения каждые 15 секунд противоречит строгим реляционным ограничениям NOT NULL и Foreign Key в боевых таблицах.

  3. Методология Contradiction-Driven Synthesis (Поиск решений под противоречия): Поиск и анализ противоречий — это постоянный навык архитектора. Когда выявлен жесткий конфликт требований, сначала ищется решение в базовых каталогах (GoF, DDD). Если стандартные шаблоны не решают конфликт идеально, на основе матрицы приоритетов синтезируется специализированное архитектурное решение.


1. Канонические и специализированные паттерны архитектуры

1. Кейс-стади Contradiction-Driven Synthesis: Рождение паттерна Dual-Mode Specification

Чтобы методология поиска противоречий не оставалась абстрактной, разберем конкретный пример того, что именно анализировалось и почему синтезированное решение помогло:

  • Анализируемое жесткое противоречие трех сил:

    • Сила А (UX веб-формы): При пошаговом вводе сложной заявки система обязана собрать и вывести на экран абсолютно все ошибки одновременно (например, 10 проблемных полей из 20), не прерывая выполнение выбрасыванием исключения при первой ошибке.

    • Сила Б (Доменная строгость Valid by Construction): При конструировании финального доменного объекта конструктор обязан мгновенно отклонить невалидное состояние (выбросить DomainException при первой ошибке), чтобы невалидный объект принципиально не мог существовать в памяти.

    • Сила В (Единая точка правды / DRY): Бизнес-правила запрещено дублировать (писать отдельные проверки для UI и отдельные if-guard проверки внутри конструкторов домена).

  • Почему стандартные шаблоны не подошли: Стандартный паттерн Спецификация (GoF) или классическая валидация контроллера решают только одну сторону (либо мягкий сбор ошибок для UI, либо жесткий Guard для домена).

  • Синтезированное решение (Dual-Mode Specification): Поскольку форма черновика и финальный агрегат имеют совпадающую структуру полей, был введен общий интерфейс чтения (DomainReadContractInterface). Бизнес-правила реализуются один раз против этого интерфейса и запускаются через исполнитель (SpecificationEvaluator) в двух разных режимах:

    1. Collector Mode (для UI-контроллера): прогоняет объект через правила, накапливает все ошибки в List<ValidationError> и возвращает их для отображения на форме без падения.

    2. Guard Mode (для фабрики доменной сущности): прогоняет объект через те же правила при конструировании. Если список ошибок не пуст, выбрасывает DomainValidationException.

  • Почему это помогло: Противоречие было полностью разрешено: бизнес-правила написаны ровно 1 раз (DRY), UX формы показывает все ошибки разом, а доменные сущности на 100% защищены по принципу Valid by Construction.

2. Базовый каталог паттернов для смежных легаси-кейсов

В зависимости от вскрытых на Фазе 0 противоречий, применяются следующие канонические абстракции:

  • Изоляция и миграция легаси:

    • Strangler Fig / Branch by Abstraction: Плавное переключение вызовов со старого монолита на новый чистый модуль без переписывания системы за раз.

    • Shadow Read-Write (Dark Launch): Параллельная запись в старую и новую схему со сверкой результатов в фоне.

  • Укрощение процедурной сложности:

    • Strategy / Pipeline: Замена многоэтажных if-else и монолитных скриптов на цепочки взаимозаменяемых независимых классов-шагов.

    • Facade: Единая чистая точка входа над клубком легаси-функций.

  • Целостность и надежность данных:

    • Immutable Type-State Builder: Моделирование пошаговых переходов через readonly class с возвратом новых экземпляров (исключает побочные эффекты мутации in-place).

    • Anti-Corruption Layer (ACL): Изоляция сервисов от сырых HTTP-массивов ($_POST, JSON) через строгие DTO.

    • Typed Collection (List<T>): Обертывание списков дочерних сущностей для 100% навигации в IDE.

    • Unit of Work / Transactional Outbox: Атомарная запись агрегата и гарантированная отправка внешних событий без потери данных при сбоях.


2. Теоретический фундамент: Функциональный DDD Скотта Влашина

Методология паттернов опирается на 5 столпов книги Скотта Влашина «Domain Modeling Made Functional»:

  1. Язык предметной области через типы: Бизнес-состояния должны быть выражены типами (Enum, Readonly Struct), а не примитивными строками.

  2. Make Illegal States Unrepresentable: Невалидный доменный объект не должен существовать в памяти.

  3. Бизнес-процессы как чистые функции: \text{Workflow}: \text{Input} \longrightarrow \text{Result}<\text{Output}, \text{Errors}>.

  4. Защита границ: Очистка и валидация данных происходит на внешней границе системы (ACL).

  5. Value Semantics: Отказ от мутации состояния в пользу функциональных трансформаций.


3. Методология безопасного тестирования и рефакторинга легаси по Майклу Физерсу (Michael Feathers)

Любая архитектурная трансформация легаси-кода обязана подчиняться правилам книги Майкла Физерса «Working Effectively with Legacy Code»:

1. Главный постулат Физерса

«Легаси-код — это просто код без тестов. Код без тестов плох тем, что его невозможно изменить с гарантией безопасности.»

2. Закон обратной пропорции качества легаси и затрат на начальное тестирование

Чем хуже качество легаси-кода (высокая запутанность, отсутствие типов, глобальные зависимости), тем больше времени и усилий придется потратить на начальное характеризационное тестирование и ручное создание эталонов (Golden Master).

Эта первоначальная инвестиция времени окупается вдвойне:

  1. Защита от регрессий: Создает надежный каркас безопасности перед декомпозицией.

  2. Археологическая раскопка домена (Выявление скрытых намерений): В процессе ручной фиксации эталонов изменений вскрываются скрытые бизнес-намерения, забытые политики и крайние случаи оркестрации, которые извлекаются из процедурного мусора и прописываются в новом чистом коде в явном виде.

3. Стратегия характеризационного тестирования (Golden Master)

  1. Захват эталона (Golden Master Capture): Направьте на старый скрипт репрезентативные наборы входных данных ($_POST / $_GET).

  2. Фиксация побочных эффектов: Запишите точные SQL-запросы, изменения в БД и сгенерированный HTML.

  3. Сверка при рефакторинге: Запустите новый контроллер на тех же входных пейлоадах и убедитесь, что доменные результаты на 100% совпадают с эталоном.

4. Швы (Seams) и Ростки (Sprout Class / Sprout Method)

  • Внедрение швов (Object Seams): Замените прямые вызовы глобальных объектов на внедряемые интерфейсы (DbConnection, LoggerInterface).

  • Классы-ростки (Sprout Class): Выращивайте новую чистую функциональность в виде изолированных, 100% покрытых тестами классов-ростков и делегируйте им работу из легаси.


4. Пошаговый протокол выполнения

Шаг 0: Диалог намерений и противоречий (Intent & Contradiction Discovery)

  • Выясните ключевые бизнес-приоритеты заказчика и ограничения инфраструктуры продакшена.

  • Выявите и озвучьте архитектурные противоречия. Подберите или синтезируйте паттерны для их разрешения.

Шаг 1: Аудит источника правды, характеризационные тесты и выявление скрытых намерений

  • Изучите исходные легаси-файлы. Потратьте необходимое время на построение эталонов (Golden Master) по Майклу Физерсу.

  • Извлеките скрытые бизнес-намерения из поведения старого кода, чтобы зафиксировать их в новой архитектуре в явном виде.

Шаг 2: Создание Agile Spike на Rust

  • Напишите компактный концепт на Rust (ARCHITECTURE_CONCEPT.rs). Опишите чистые структуры полей без гетеров, перечисления этапов UI и домена, стратегии восстановления (ErrorRecoveryStrategy) и трейты репозиториев.

Шаг 3: Проектирование слоя ACL, типизированных коллекций (List) и швов

  • Перенесите структуры в целевой язык в виде строгих readonly DTO. Замените массивы на списки List<T>.

  • Внедрите объектные швы (Seams) и ACL для изоляции домена от HTTP и глобальных переменных.

Шаг 4: Реализация универсального доменного ядра по Влашину и классам-росткам

  • Создайте общие интерфейсы чтения (DomainReadContractInterface), атомарные правила (SpecificationRule) в виде классов-ростков и иммутабельные агрегаты (ValidAggregate).

Шаг 5: Изоляция представления и настройка IDE-навигации

  • Вынесите вёрстку и подсветку ошибок в изолированный класс вёрстки (Render). Настройте единую точку входа (public/index.php), автозагрузчик и фабрику DI для 100% навигации в IDE.

Шаг 6: Защита продакшена, транзитные исключения и сверка с Golden Master

  • Проставьте точные аннотации @throws во всей цепочке вызовов. Постройте эшелоны отказоустойчивости.

  • Прогоните характеризационные тесты и убедитесь в 100% совпадении с эталоном.


5. Антипаттерны (Чего делать КАТЕГОРИЧЕСКИ НЕЛЬЗЯ)

Blind Coding without Phase 0: Написание кода или выбор паттернов без предварительного выявления намерений, приоритетов и противоречий заказчика.

Refactoring without Characterization Tests: Изменение легаси-кода без инвестиции времени в фиксацию эталонного поведения (Golden Master) и выявления скрытых намерений.

Legacy Mindset / Primitive Obsession: Использование сырых ассоциативных массивов (array $items) вместо DTO и типизированных коллекций List<T>.

Duplicated Validation: Написание отдельных проверок для UI-формы и отдельных проверок внутри конструкторов домена.

Anemic Valid Entity: Создание готовой сущности, которая является простой оберткой над DTO без проверки инвариантов при конструировании.

In-Place Mutation: Прямое изменение полей ($draft->items[] = ...) вместо иммутабельных функциональных трансформаций.

Boolean Blindness: Использование примитивных флажков bool $isError вместо перечислений (ErrorRecoveryStrategyEnum).

ссылка на оригинал статьи https://habr.com/ru/articles/1055616/