Статьи

Код очистки: стоит ли рефакторинг для эстетики?

Большинство команд разработчиков хотят привести свою кодовую базу в лучшее, более удобное для обслуживания состояние. Но какое определение лучше выбрать? Во многих случаях нет необходимости углубляться в доменно-управляемый дизайн (DDD) для достижения этой цели. Иногда это даже контрпродуктивно. Но один из самых основных наборов принципов уже может помочь каждой команде: чистый код.

Векторное изображение подметальной метлы

Цель

В книге « Чистый код » Роберта С. Мартина обобщено множество простых и расширенных улучшений, позволяющих получить лучший, понятный и, следовательно, более понятный код. Давайте возьмем следующий фрагмент кода в качестве примера:

public function getJobs($date)
{
    $ret = array();
    $res = $this->db->query(
        'SELECT * FROM jobs WHERE year=' . date('Y', $date) . ' AND month='
            . date('m', $date)
    );
    $res = $res->fetchAll();
    foreach ($res as $data) {
        $j = new Job(/* $data['...'] */);
        $j->rev = $j->time * $j->euro;
        $ret[] = $j;
    }
    return $ret;
}

Похоже, что цель этого и без того простого метода — вернуть задания на определенную дату. Хотя это намерение очевидно, прочитать метод и понять, как он выполняет свою работу, не так просто. После этого алгоритм выглядит довольно просто: сначала он выбирает некоторые данные из базы данных, затем он подготавливает объекты заданий, вычисляет некоторые дополнительные данные и возвращает объекты.

Но время, потраченное на понимание этого простого фрагмента кода, потрачено впустую. Как разработчик, анализируя свою повседневную работу и работу других людей, вы заметите, что тратите гораздо больше времени на чтение и понимание кода, чем на его написание.

Низко висящим плодом будет просто переименовать локальные переменные для улучшения читабельности:

 public function getJobs($date)
{
    $jobs = array();
    $statement = $this->db->query(
        'SELECT * FROM jobs WHERE year=' . date('Y', $date) . ' AND month='
            . date('m', $date)
    );
    $rows = $statement->fetchAll();
    foreach ($rows as $row) {
        $job = new Job(/* $row['...'] */);
        $job->rev = $job->time * $job->euro;
        $jobs[] = $job;
    }
    return $jobs;
}

Переменная, ранее известная как $ret$jobs Это имя теперь ясно указывает на то, что мы можем ожидать от него: список рабочих мест. Кроме того, это не может быть легко перепутано с $ret$statement$rows Теперь мы понимаем, что происходит намного лучше.

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

 public function getJobs($date)
{
    $statement = $this->db->query(
        'SELECT * FROM jobs WHERE year=' . date('Y', $date) . ' AND month='
            . date('m', $date)
    );
    $rows = $res->fetchAll();

    $jobs = array();
    foreach ($rows as $row) {
        $job = new Job(/* $row['...'] */);
        $job->rev = $job->time * $job->euro;
        $jobs[] = $job;
    }
    return $jobs;
}

Теперь есть два абзаца. В первом есть ряд строк, выбранных из базы данных. Второй абзац преобразует эти строки в объекты. Это незначительное перемещение объявления $jobs

Это только верхушка айсберга, которую предлагает Чистый код.

Методология

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

  1. Выберите 1 единственный шаг рефакторинга
  2. Выполнить выбранный шаг
  3. Убедитесь, что код все еще работает
  4. Зафиксировать текущее состояние и перейти к 1

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

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

Процесс также позволяет вам прекратить рефакторинг в любой момент времени без потери прогресса. Срочную ошибку, чтобы исправить? Коллега просит спарринг? Обед? Нет проблем, вы можете либо просто объединить имеющееся у вас рабочее состояние, либо оставить его в ветке, чтобы потом снова поднять его.

Тем не менее, есть один недостаток с частыми коммитами во время рефакторинга: так много маленьких коммитов может загрязнить вашу историю контроля версий. Если вы используете Git, вы можете смягчить это, сгруппировав коммиты рефакторинга в один, перед тем как отправлять вверх по течению.

Предварительные условия

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

Прежде всего, не стоит проводить рефакторинг только потому, что вы можете или потому что считаете, что текущий код выглядит некрасиво. Изменение кода — это всегда риск, и вы не должны рисковать без веской причины. Например, одной из причин может быть то, что вам часто приходится работать над одним фрагментом кода, который трудно понять и где каскадные изменения происходят в вашей системе. Но есть много других причин, таких как частые ошибки, непредвиденные бизнес-требования и т. Д. Важно тщательно проанализировать причины, а не просто слепо столкнуться с рефакторингом.

Во-вторых, важно иметь хотя бы некоторую степень автоматических тестов, охватывающих наиболее важные бизнес-случаи, когда используется рефакторинг кода. Устаревшие системы обычно сложно тестировать с помощью модульных или интеграционных методов тестирования. Поэтому рекомендуется не пытаться применять эти методы с помощью подхода грубой силы. Вместо этого мы сочли весьма полезным применять тесты через интерфейс — в нашем случае, используя Mink и PHPUnit.

Но, как и многие другие решения в разработке программного обеспечения, это только практические правила, и у каждого проекта есть свои требования и приоритеты. Присоединяйтесь ко мне на семинаре по чистому коду на WebSummerCamp 2016, чтобы узнать больше. Возьмите с собой компаньона — есть специальная программа для других значимых людей, так что приведите компанию и сделайте ее рабочим отпуском!