Несколько приемов и советов по работе с легаси-кодом

в 19:13, , рубрики: Без рубрики

Несколько приемов и советов по работе с легаси кодом

Данная статья является компиляцией моего собственного опыта и знаний полученных из различных статей и книг, в частности «Эффективная работа с унаследованным кодом» Майкла Физерса. Стоит оговориться, что опыт мой связан только с работой с динамическими языками, поэтому не все пункты в списке применимы для всех разработчиков.

1. Не делите код на старый и новый

Хотя в статье неоднократно упоминается это разделение, не стоит делить код по времени. Любой новый код через неделю становится старым, если не покрыт тестами, и часто понять его еще сложнее, чем код написанный год назад. Код следует делить на оттестированный и неоттестированный. Соответственно неоттестированный код и будет являться легаси.

2. Почкование методов

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

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

Минусы:

  • далеко не всегда создание нового метода является лучшим выбором с точки зрения общей архитектуры
3. Почкование классов

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

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

Минусы:

  • нарушение общей архитектуры классов
  • не всегда небольшие добавления в функциональности являются новой «ответственностью»
4. Охват метода и охват класса

Применяется в более простой ситуации, когда изменения не нужно вставлять в середину действующего метода или класса. В этом случае создается новый метод(или класс), который вызывает и старый код, и новый добавленный метод. Подробнее об это приеме, как и о предыдущих можно прочитать у Физерса[1].

5. Применяйте изменения как можно чаще

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

6. Пишите интеграционные тесты

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

7.Измеряйте покрытие кода тестами

Тот факт, что строка кода покрыта тестами совершенно не значит, что она не содержит ошибок. Тестовое покрытие скорее позволяет определить участки кода, в которые не ступала нога юнит-теста. А также определить полностью «заброшенные» компоненты системы и те, где не все так ужасно.

8. Определите точки сужения

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

9. Сохраняйте сигнатуры методов

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

Если вы хотите глубже изучить данную проблему, читайте Физерса. У меня на этом все, боритесь с легаси-кодом — если вы не победите легаси-код, то однажды он победит вас.

Дополнительные ссылки

1. Michael Feathers — Working Effectively with Legacy Code
www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052

2. Kerri Miller — Harry Potter and The Legacy Code Base
speakerdeck.com/kerrizor/harry-potter-and-the-legacy-code-base-ruby-on-ales

3. Bryan Helmkamp — Gold Master Testing
blog.codeclimate.com/blog/2014/02/20/gold-master-testing/

4. David Heinemeier Hansson — Legacy Software
http://web.archive.org/web/20130729203156id_/http://itc.conversationsnetwork.org/shows/detail3987.html

Автор: rsludge

Источник

Поделиться

* - обязательные к заполнению поля