Рефакторинг
Что такое рефакторинг?
Рефакторинг — это деятельность по улучшению внутренней структуры или работы кода или компонента без изменения его внешнего поведения. Целью разработки программного обеспечения является непрерывная доставка бизнес-ценности пользователям и заинтересованным лицам.
Постоянно меняющиеся технологии и непрерывно развивающиеся бизнес-цели затрудняют поддержание и постоянное повышение бизнес-ценности. В этой ситуации существует два подхода:
- Продолжать добавлять новую функциональность в существующую кодовую базу не меняя её, доводя в итоге код до такого состояния, когда он становится «неуправляемым» и слишком дорогим для поддержки.
- Непрерывно модифицировать всю систему, создавая основу для эффективной доставки как текущей, так и будущей бизнес-ценности.
Второй подход, безусловно, лучше. Это и есть рефакторинг. Благодаря непрерывному рефакторингу срок полезного использования инвестиций предприятия в программные (нематериальные) активы может быть существенно увеличен. Таким образом, в результате непрерывного рефакторинга пользователи смогут продолжать получать новую ценность в течение многих лет.
Рефакторинг позволяет поддерживать возникающий дизайн (emergent design), гарантируя, что система сможет соответствовать будущим потребностям бизнеса. В SAFe рефакторинг — это Истории-Энейблеры. Соответственно, Истории рефакторинга обладают такими же качествами, как и любые другие Истории: 1) содержат ценность для клиента, 2) их можно оценить, 3) их можно проверить и 4) их принимает Владелец Продукта.
Здесь я бы хотел ответить на часто звучащий вопрос «а какого типа Энейблеры должны быть, в случае если это рефакторинг?».
Напомню, что SAFe рассматривает 4 базовые типа Энейблеров: Исследования, Архитектура, Инфраструктура и Соответствие (регуляторным требованиям), при этом не запрещая уточнять этот список.
Исходя из определения Рефакторинга, приведенного выше (улучшение работы «внутри» без изменений «снаружи»), улучшение работы «внутри» может относится к любому из перечисленных направлений технической разработки, хотя чаще всего будет касаться архитектуры или инфраструктуры.
Однако, многое зависит от существующей инженерной культуры и выбранной настройки управления и учета Agile требований. При настройке управления Agile требованиями важно обсудить то, как в разрезе 4-х типов Энейблеров будет на практике фиксироваться технический долг (которого, конечно, в идеале вообще быть не должно :). Связано это с грустной практикой многих организаций, лидеры которых не до конца понимают ценность непрерывного Рефакторинга, в результате чего и появляется тот самый технический долг.
Таким образом, у лидеров разработки есть выбор: создавать или нет отдельные типы Энейблеров для техдолга и/или рефакторинга, и далее, распределять или нет техдолг и рефакторинг по каким-либо стандартным типам Энейблеров SAFe.
С точки зрения «идеальной» настройки системы можно рекомендовать максимально придерживаться базовых рекомендаций SAFe по крайней мере до тех пор, пока не будет серьезных объективных причин делать какую-либо «донастройку».
Алексей Ионов, Executive Lean-Agile коуч, Ионов и Партнеры
Использование рефакторинга
На рисунке 1 показаны основы рефакторинга. Сам рефакторинг заключается в модификации любого программного объекта — модуля, метода или программы — для улучшения его структуры или жизнеспособности без изменения его внешней функциональности.


Рисунок 1. Рефакторинг в изолированной среде для внесения изменений внутри более крупного объекта
Например, одни элементы рефакторинга могут привести к увеличению скорости обработки, получению различных внутренних данных или решению проблем безопасности. Другие компоненты могут оптимизировать какие-то аспекты кода, чтобы сделать его более эффективным, удобным в обслуживании или легким для чтения.
Рефакторинг требует, чтобы каждое изменение было немедленно протестировано, чтобы убедиться в достижении желаемой цели. Процесс рефакторинга может быть разбит на последовательные «микро-рефакторинги» для достижения более масштабной цели. Каждый небольшой рефакторинг должен быть протестирован на корректность. Такой итеративный процесс позволяет сохранить целостность программного обеспечения на любом этапе.
SAFe подчеркивает важность прозрачности и видимости всей работы, которая выполняется, включая рефакторинг. Как и работа с пользовательской ценностью, рефакторинг должен быть спланирован, оценен и расставлен по приоритетам.
Источники рефакторинга
Как показано на рисунке 2, рефакторинг возникает из различных источников.


Рисунок 2. Возможные источники рефакторинга
Рефакторинг может быть спровоцирован разработкой новой бизнес-Фичи или являться частью рефакторинга более крупной инициативы, необходимой для создания нового архитектурного Энейблера. Новые пользовательские истории также могут потребовать рефакторинга кода. Технический долг может вынудить команду провести рефакторинг определенных компонентов. В каких-то случаях рефакторинг кода может быть вызван новыми нефункциональными требованиями (НФТ/NFR).
Не все действия по рефакторингу возникают из Историй. Процесс разработки на основе тестирования (Test-Driven Development, TDD) способствует непрерывному рефакторингу при внесении изменений в код. Разработчики постоянно обновляют дизайны, чтобы лучше соответствовать текущим и будущим требованиям. Такая работа должна быть учтена в оценке соответствующей Истории. Однако некоторые элементы рефакторинга представляют собой более значимые части редизайна, которые необходимо планировать и отслеживать как отдельные элементы беклога.
Как описать рефакторинг?
Для того, чтобы добиться результатов, очень важно понимать, какая ценность будет достигнута после завершения рефакторинга. Для этого команды могут подготовить Энейблер историю, написанную в формате, максимально похожем на «Голос клиента», который используется для написания Пользовательских историй. Таким образом, члены команды смогут получить единое понимание «зачем мы это делаем» (цель) и «что мы получим» (ценность), как показано на рисунке 3:


Рисунок 3. Пример Энейблер Истории рефакторинга
Оптимизация рефакторинга
Как и при работе с Пользовательскими Историями, важно разделять Истории рефакторинга на более мелкие элементы. Такой подход помогает эффективнее поддерживать поток разработки. Далее мы рассмотрим некоторые практические методы оптимизации рефакторинга и примеры для каждого из них.
Методы разделения (декомпозиции) Историй рефакторинга
1. По пользовательскому сценарию или пользовательской истории – Делайте рефакторинг инкрементально в режиме «история-за-историей» или «сценарий-за-сценарием». Или каким-то другим способом определите функциональные области в качестве основы для инкрементальной работы.
Энейблер: Усовершенствовать запросы к БД и внедрить кэширование данных, таким образом система будет работать быстрее.
Действие: Провести рефакторинг функциональности «Управление пользователями», каталогизировать функциональность просмотра, проверить работоспособность и быстродействие.
2. По компонентам – Провести рефакторинг всего, что связано с одним компонентом, а затем перейти к следующему.
Энейблер: Заменить процесс индексации на пакетную обработку, таким образом процесс ускорится как минимум в 2 – 3 раза по сравнению со среднем времени индексации одной веб-страницы.
Действие: сначала сделать парсинг (компонент парсера), затем извлечение сущностей (компонент анализатора), затем хранение в индексе (компонент индекса)
3. Интерфейс / Реализация – Сначала создайте новые интерфейсы, подключите к ним старый функционал, а затем проведите рефакторинг функционала.
Энейблер: Переместить все параметры парсинга в XML файл конфигурации, таким образом процесс можно будет легко настраивать без изменения кода.
Действие: Установить сервер с протоколом PINGID и протестировать его на заглушке фиктивного поставщика идентификационных данных…Прочитать конфигурации из файла в любом формате. Сделать рефакторинг функциональности конфигурации для поддержки определенной структуры и валидации схемы.
4. «Удушение»/Вытеснение компонента – Постепенно переносите функциональность компонента на другие компоненты. Как только все будет перемещено, удалите старый код.
Энейблер: Заменить базу данных пользовательским индексом поиска, таким образом эффективность индексирования и поиска повысится в 10–20 раз.
Действие: Сначала переместить данные индекса в пользовательский поисковый индекс. Затем переместить словари сущностей.
5. Встроенный рефакторинг / Извлечение – Выполните рефакторинг функциональности в том месте, где она находится в настоящее время, но затем извлеките ее и инкапсулируйте в компонент, класс или метод/функцию.
Энейблер: Заменить текущий ситуативный парсинг функциональностью, основанной на грамматике, таким образом изменение правил парсинга станет простым и не будет требовать написания кода.
Действие: Рефакторинг кода (как есть) для использования грамматической нотации. Извлечение всех функций, связанных с грамматикой, в грамматический движок.
Критерии приёмки для рефакторинга
Определение критериев приёмки для рефакторинга помогает устранить двусмысленность и неточность, как и в случае с пользовательскими Историями. На рисунке 4 показан пример уточнений, которые становится возможным установить через критерии приёмки.


Рисунок 4. Пример критериев приёмки для истории рефакторинга
Критерии приёмки часто используются как основа для декомпозиции рефакторинга на более мелкие элементы. Например, первым шагом разработки на рисунке 4 может быть запуск синхронной неконфигурируемой обработки пакета запроса с единым обращением к словарю, но без ведения журнала отладки. Затем можно добавить возможность «считывать» размер этого пакета из файла. На шаге 3 элементы процесса будут выполняться асинхронно. А на финальном этапе можно добавить функциональность ведения журнала отладки.
Демонстрация рефакторинга
Несмотря на то, что рефакторинг ориентирован на внутреннюю работу кода, команды демонстрируют результаты, как и при работе с любой другой Историей. Из приведённого выше примера команды могут показать следующее:
- Сокращение времени обработки нескольких веб-страниц по сравнению с предыдущим показателем
- Зависимость времени обработки от размера запроса (партии), который можно настроить из файла
- Фрагмент кода для асинхронной обработки
- Файл журнала отладки, в котором фиксируются все операции
- Количество запросов к словарям внутри каждой партии (из файла журнала)
Внедрение культуры рефакторинга
Рефакторинг является необходимым навыком для Agile команд и критически важным элементом компетенции «Командная и Техническая Гибкость» для построения бизнес-гибкости организации. Рефакторинг должен регулярно появляться в беклоге команды и включаться — вместе со встроенным рефакторингом — в оценки Историй. Создание Сообщества практиков в области дизайна (CoP) помогает расширить знания о методах рефакторинга и повысить их применение в работе. Скрам-мастера / Коучи команд могут помочь своим командам освоить эффективные подходы к определению, оценке и декомпозиции элементов рефакторинга. Владельцы продукта должны поддерживать рефакторинг, расставляя приоритеты в работе и помогая командам определить критерии приёмки.
Культура автоматизации тестирования, включая Разработку на основе тестирования (Test-Driven Development, TDD) и Разработку на основе поведения (Behavior-Driven Development, BDD), создает большой набор тестов, которые делают рефакторинг более доступным и надежным. При использовании TDD/BDD ошибки, внесенные рефакторингом, будут немедленно отлавливаются тестами, что позволит гарантировать ценность и снизит риски изменения кода, уже используемого бизнесом.
Статья подготовлена по материалам Scaled Agile, Inc. и не является официальным переводом статьи «Refactoring». Статья подготовлена по последней версии статьи на сайте вендора от 24.02.2023.