Рефакторинг
Про обязательный технический навык гибкости команд: как определять, декомпозировать и показывать результаты рефакторинга.
Вольный перевод статьи: Refactoring — Scaled Agile Framework.
Необходимо, чтобы у вас было руководство к действию, которое вело бы вас в нужном направлении до тех пор, пока бы вы не научились это делать сами. Именно пренебрежение своевременными правками делает необходимым разработку заново. —Ричард Уэйтли
Рефакторинг — это работы по улучшению внутренней структуры, а также производительности кода или целого компонента без изменения их внешнего поведения.
Целью продуктовой разработки является непрерывная поставка бизнес-ценности пользователям и заинтересованным лицам. Постоянно меняющиеся технологии в сочетании с меняющимися бизнес-целями значительно затрудняют это.
Существует два подхода, как можно действовать в такой ситуации:
- Продолжать добавлять новые Фичи в существующую кодовую базу до тех пор, пока она не дойдет до неподдерживаемого состояния. После чего ее по сути придется просто выбросить.
- Постоянно улучшать код, чтобы поддерживать фундамент эффективной поставки не только текущей, но и будущей бизнес-ценности продукта.
Второй вариант, а именно он является рефакторингом, является предпочтительным.
Благодаря непрерывному рефакторингу жизненный цикл инвестиций компании в продукты может быть продлен на максимально долгий срок. А пользователи смогут долгие годы получать ценность от таких продуктов.
Рефакторинг позволяет спроектировать код под внезапно всплывающие изменения так, чтобы система была способна удовлетворять будущие потребности бизнеса. Рефакторинг — помимо концепции, это еще и особый тип Enabler-истории в SAFe®. Как и любые другие Enabler они должны быть оцениваемыми, обозримыми и ценными, а также принимаемыми Владельцем Продукта.
Рисунок 1 иллюстрирует суть рефакторинга как модификацию любой сущности кодовой базы: модуля, метода или приложения — как для улучшения его структуры, так и для жизнеспособности без изменения внешней функциональности.
Рисунок 1. Рефакторинг в изолированной среде для внесения изменений в рамках более крупной сущности
Например, рефакторинг может улучшать такие аспекты, как увеличение скорости обработки, получение данных из различных источников или повышение безопасности. Другой тип рефакторинга оптимизирует определенные аспекты кода, чтобы сделать его более эффективным, более удобным в обслуживании или более читаемым.
Рефакторинг требует немедленного тестирования каждого изменения, чтобы проверить достигнута ли желаемая цель. Рефакторинг может быть разбит на серию микро-рефакторингов, чтобы последовательно достичь общей цели. При этом каждый микро-рефакторинг должен быть протестирован на корректность. Такой итеративный процесс помогает сохранить работоспособность продукта на любом этапе рефакторинга.
SAFe подчеркивает важность того, чтобы вся работа, включая рефакторинг, была видимой для всех участников. Как и фичи, рефакторинг необходимо планировать, оценивать и приоритезировать на всех уровнях Решения.
Источники рефакторинга
Рефакторинг, как задача, может появиться из различных источников, как это показано на Рисунке 2.
Рисунок 2. Возможные источники рефакторинга, как отдельной задачи
Рефакторинг может быть востребован при реализации Фичи или быть частью крупной инициативы по рефакторингу, необходимой для каких-то архитектурных Enabler. Новые пользовательские Истории также могут потребовать некоторого рефакторинга кода. Накопленный технический долг также может подтолкнуть команду к рефакторингу определенных компонентов. Другие же задачи по рефакторингу могут быть вызваны новыми Нефункциональными Требованиями.
Не все подобные задачи исходят из пользовательских историй. Иными словами мы не всегда рефакторим только для того, чтобы это разблокировало создание фичей. Например, Test-Driven Development (TDD) подталкивает нас к непрерывному рефакторингу, как части процесса по изменению кода. То есть разработчики должны постоянно править и улучшать уже разработанный код с тем, чтобы он в лучшей степени отвечал текущим и будущим требованиям. Такая работа должна быть заложена при оценке соответствующей Истории. Однако, нужно учитывать, что отдельно сформулированные задачи на рефакторинг, могут быть частью более серьезного редизайна, который необходимо планировать и отслеживать как отдельный элемент бэклога.
Определение рефакторинга
Важно понимать, какая ценность будет получена после того, как рефакторинг будет выполнен. Для этого команды могут использовать часть «…, чтобы…» («…so that…») из описания пользовательской истории. Это даст общее понимание цели и ценности рефакторинга, например так, как это показано на Рисунке 3.
Рисунок 3. Пример задачи по рефакторингу
Декомпозиция рефакторинга
Как и для пользовательских историй, разбиение на подзадачи важно и для рефакторинга, так как это помогает поддерживать постоянный поток процесса разработки. В Таблице 1 представлены некоторые полезные методы для декомпозиции задач по рефакторингу и пример по каждому из них.
1. По пользовательским сценариям или пользовательским историям — рефакторить инкрементально историю за историей, или сценарий за сценарием, или каким-то иным способом выделить несколько функциональных областей для их последовательного рефакторинга. | |
Оптимизировать запросы к БД и внедрить кеширование данных, чтобы система работала быстрее | …Отрефакторить все функции управления пользователями… Далее функционал просмотра каталога… Далее функционал проверки работоспобности |
2. По компонентам — сначала провести рефакторинг кода одного компонента, а потом перейти к следующему. | |
Поменять процесс индексирования на пакетную обработку, чтобы процесс был как минимум в 2–3 раза быстрее для средней веб-страницы | …Рефакторим парсинг (компонент парсера)… Далее экстрактор сущностей (компонент-анализатор)… Далее хранение в индексе (компонент индекса) |
3. По интерфейсам/реализациям — сначала создаем интерфейс и оборачиваем им старую функциональность, а потом рефакторим саму функциональность. | |
Извлечь все параметры синтаксического анализа в xml-файл конфигурации, чтобы процесс можно было легко настроить без изменения кода | …Ставим PINGID Protocol server и тестим его с помощью провайдера идентификации mock…Читаем конфигурацию из файла в любом формате… Рефакторим функциональность конфигурирования, чтобы поддерживать в должном состоянии валидацию структуры и схемы |
4. Уменьшение устаревшего кода / Разделение монолита — постепенно переносим функциональность из legacy-компонента (компонент с устаревшим кодом) в новые; как только все будет перемещено, удаляем старый код. | |
Заменить базу данных пользовательским поисковым индексом, чтобы производительность индексирования и поиска повысилась в 10-20 раз | …Сначала переносим индексацию данных в поисковый индекс… Далее переносим словари |
5. Точечный рефакторинг / Инкапсуляция результата – сначала рефакторим там, где это нужно, но потом извлекаем измененную сущность и инкапсулируем ее в компонент, класс или метод/функцию. | |
Заменить ad hoc синтаксический анализ на анализ на основе грамматики так, чтобы изменение правил синтаксического анализа стало простым и без необходимости кодирования | …Рефакторим текущий код так, чтобы он использовал нотацию грамматик… Извлекаем эту функциональность и помещаем ее в движок грамматик |
Таблица 1. Методы разбиения задач по рефакторингу
Определение критериев приемки
Как и в случае с пользовательскими историями, определение критериев приемки для задач по рефакторингу помогает устранить двусмысленность. На рисунке 4 показана специфичность критериев приемки для таких задач.
Рисунок 4. Пример критериев приемки по рефакторингу
Критерии приемки вполне можно использовать как способ разбиения задачи по рефакторингу.
Если взять пример на рисунке 4, то первым шагом рефакторинга может быть «…выполнить синхронную неконфигурируемую пакетную обработку с одним запросом к словарю, но без логирования». Затем «…добавить возможность считывать размер пакета из файла». Далее будет«…обрабатывать элементы асинхронно» и, наконец, «…добавить логирование».
Демонстрация результатов рефакторинга
Несмотря на то, что рефакторинг ведется на уровне кода (под капотом), как и для любой другой пользовательской истории, команды должны иметь возможность продемонстрировать результаты.
Если брать пример выше, то команды могли бы это сделать так:
- Уменьшено время обработки на нескольких веб-страницах по сравнению с предыдущим эталонным тестом.
- Понятна зависимость времени обработки от размера пакета, который теперь можно прочитать из файла.
- Есть сниппет кода кода для возможности асинхронной обработки.
- Есть файл логирования, в котором фиксируются все операции.
- Количество запросов к словарям из расчета на один пакет (по статистике из лога).
Принятие культуры рефакторинга
Рефакторинг — это обязательный навык для Agile-команд и критически важный компонент компетенции Командной и Технической Гибкости в Lean-организации. Задачи по рефакторингу должны регулярно появляться в бэклоге команды и включаться, наравне с точечным рефакторингом, в оценку пользовательских историй. Создание Community of Practice (CoP) может повысить осведомленность и внимание к методам рефакторинга среди команд. Scrum-мастера могут помочь своим командам изучить эффективные подходы к тому как описывать, оценивать и разбивать задачи по рефакторингу. Владельцы Продуктов должны соглашаться на рефакторинг, корректно расставляя приоритеты работ и помогая определять критерии приемки.
Культура автоматизации тестирования, включая TDD и Behavior-Driven Development (BDD), дает возможность собрать большой набор тестов, которые сделают рефакторинг проще и надежнее. При этом ошибки, вносимые рефакторингом, сразу же будут выявляться тестами.
SAFe and Scaled Agile Framework are registered trademarks of Scaled Agile, Inc.
Руководство по работе с функциональными и техническими исследовательскими задачами.
Описание воркшопа по созданию компонентной структуры продукта