Как аккуратно работать с legacy-кодом

Legacy-код — это не обязательно плохой код. Чаще это код, который долго жил, много раз менялся и решал реальные задачи бизнеса. Проблема начинается, когда его пытаются быстро “причесать” без понимания связей. Старый проект обычно ломается не от большой ошибки, а от маленькой правки в месте, которое оказалось общим для половины сайта.

Сначала понять границы задачи

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

  • какая конкретно функция меняется;
  • какие страницы или команды её используют;
  • какие данные считаются входными;
  • какой результат считается правильным;
  • есть ли похожие места в проекте.

На практике полезно прямо выписать сценарии до правки. Это занимает меньше времени, чем потом искать, почему “раньше работало”.

Зафиксировать текущее поведение

Даже если тестов нет, можно сделать минимальную фиксацию: сохранить примеры входных данных, результаты расчёта, скриншоты админки, SQL-выборки. Это не идеальная методология, но она помогает не спорить с памятью.

SELECT id, sku, price, old_price
FROM products
WHERE sku IN ('A100', 'A101', 'A102');

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

Не начинать с большого рефакторинга

Рефакторинг нужен, но в legacy-проекте его лучше делать маленькими шагами. Сначала выделить понятный метод, затем убрать дублирование, потом добавить проверку. Попытка сразу построить красивую архитектуру часто заканчивается недельной стабилизацией.

Например, старый код может выглядеть так:

$price = $row['price'];
if ($row['discount'] > 0) {
    $price = $price - ($price * $row['discount'] / 100);
}
if ($row['extra_fee'] > 0) {
    $price = $price + $row['extra_fee'];
}

Первый аккуратный шаг — не переписывать весь модуль, а вынести расчёт в отдельный метод и покрыть его несколькими примерами.

private function calculateFinalPrice(array $row)
{
    $price = (float)$row['price'];

    if ((float)$row['discount'] > 0) {
        $price -= $price * (float)$row['discount'] / 100;
    }

    if ((float)$row['extra_fee'] > 0) {
        $price += (float)$row['extra_fee'];
    }

    return round($price, 2);
}

Искать скрытые зависимости

В старых проектах одна функция может использоваться в неожиданных местах: в публичной части, админке, cron, импорте, API и старом AJAX-обработчике. Перед изменением стоит поискать вызовы.

grep -R "calculateFinalPrice" -n .
grep -R "old_function_name" -n .

Если проект на Yii2, дополнительно нужно проверить маршруты, консольные команды, события, behaviors и компоненты в конфиге. Не весь код вызывается напрямую из контроллера.

Логировать спорные места

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

Yii::info([
    'product_id' => $productId,
    'old_price' => $oldPrice,
    'new_price' => $newPrice,
], 'price-recalc');

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

Работать через маленькие коммиты

Маленький коммит проще проверить и проще откатить. Для legacy-кода это особенно важно. Один коммит “поправил расчёт, обновил верстку, перенёс классы и удалил старые файлы” почти невозможно быстро разобрать при ошибке.

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

Когда всё-таки нужен рефакторинг

Рефакторинг оправдан, когда код регулярно мешает изменениям или содержит явные риски: дублирование расчётов, копии SQL-запросов, смешение HTML и бизнес-логики, неочевидные глобальные переменные. Но даже тогда лучше двигаться постепенно.

  1. Найти конкретный болезненный участок.
  2. Описать текущее поведение на примерах.
  3. Вынести кусок логики в отдельный метод или класс.
  4. Проверить старые сценарии.
  5. Только после этого убирать дубли.

Минимальный чек-лист перед правкой

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

С legacy-кодом лучше работать спокойно. Не нужно относиться к нему как к мусору только потому, что он старый. Сначала понять, какую задачу он решает, затем аккуратно уменьшить хаос в одном месте. Такой подход обычно даёт больше пользы, чем резкая перепись без полной картины.

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

Пока нет комментариев. Будьте первым!