Feature toggling что это

Что такое feature toggle или как избавиться от мучительных мёржей и долгоживущих веток?

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

Проблема

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

Использование feature switcher-ов для решения проблем

Такая проблема встречается в разработке довольно часто и есть изящное решение, позволяющее взять лучшее от описанных выше подходов — feature toggle или feature switcher.

По сути, feature switcher — это boolean флаг, который хранится в базе данных и содержит информацию о том, должна быть включена та или иная фича или нет. Значение этого флага может быть извлечено из базы данных по ключу. Удобство использования feature switcher-ов заключается в том, что они могут быть легко изменены бизнес-пользователем во время runtime через панель администратора без необходимости заново деплоить приложение.

Ниже приведен пример использования feature toggle на языке Java:

В примере выше configurationManager — это класс, позволяющий извлечь значение определенного feature switcher-а из базы данных по его ключу.

Также, при помощи feature switcher-ов, можно отображать/скрывать определенные элементы на фронтенде. Для этого придется положить значение флага в Model и передать его на View как это показано ниже:

После чего использовать переданное значение для рендеринга того или иного HTML кода:

Виды feature switcher-ов

Описанный концепт использования feature switcher-ов — это лишь один возможный случай использования и такие feature switcher-ы называются release toggles. Всего выделяют 3 разных вида feature switcher-ов:

Feature toggling что это. Смотреть фото Feature toggling что это. Смотреть картинку Feature toggling что это. Картинка про Feature toggling что это. Фото Feature toggling что это

Проблемы использования feature toggle-ов

Поскольку я работаю на проекте, где активно используются feature toggle-ы, то кроме очевидных достоинств их использования я начал замечать и проблемы, связанные с ними:

Решения некоторых из описанных проблем

Помочь решить вышеописанные проблемы могут следующие действия:

Итоги

Feature switcher — очень простой и одновременно мощный механизм, позволяющий избегать монструозных коммитов, легко менять поведения приложения или собирать несколько разных приложений на одной кодовой базе, используя разную конфигурацию feature toggle-ов.

Однако, стоит также помнить, что этот паттерн разработки имеет некоторые недостатки, которые выливаются в трудночитаемый и трудно поддерживаемый код, поэтому следует избегать чрезмерного использования этого паттерна и периодически проводить документирование feature switcher-ов и их ревизию, чтобы удалять неиспользуемые и, как следствие, очищать проект от “мёртвого” кода.

Источник

Feature toggle

Feature toggling что это. Смотреть фото Feature toggling что это. Смотреть картинку Feature toggling что это. Картинка про Feature toggling что это. Фото Feature toggling что это

Подготовил репку с примерами, смотреть тут.

Предыстория

Мне нравится англоязычный подкаст по iOS-разработке iPhreaks, и в один прекрасный момент я набрел на соседний рубевый подкаст про feature toggles. Все началось с легкого наброса про то, что не стоит ребейсить, продолжилось про интереснейший подход к ведению веток TBD и закончилось feature toggles.

Само знакомство с последним подходом стало для меня некоторым откровением. Причина скорее всего в том, что самому приходилось удалять фичи, которые были реализованы несколько месяцев назад, причем это делалось с муками и болью, а тут мне предложили отключение фич по щелчку пальцев, да еще и наобещали A/B тестирования с удаленным управлением фичами.

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

В процессе подготовки читал разные источники, но ключевым стал этот пост в блоге Фаулера, там автор разбирает данный вопрос с примерами из js, я же попробовал адаптировать это к реалиям iOS-разработки, естественно, ничего теоретически нового я не придумал.

Забегая вперед, напишу, что позже с этой темой я выступил на локальной Рамблеровской конфе Rambler.iOS, а потом и на питерской Mobius. Надо сказать, что как раз на последней народ принял довольно прохладно, и в комментах было и про банально, и про то что не надо, а если надо, то делается просто. Отдельно были комменты про скомканный материал(мы с коллегой решили уместиться в один слот, чтобы народ не скучал, видимо получилось слишком бодро) и про то, что слишком много теории, нужна практика и примеры.

Про просто я не согласен(иначе не встречался бы код где переключение фичи размазано по всему приложению), а вот недостаток практики и примеров попробую исправить. Кто уже устал читать может глянуть сразу в пример на github.

Теория

Концепция

В данном подходе есть несколько основных моментов:

Категории фич

В том самом посте из блога Фаулера, довольно долго расписываются категории с диаграммами Венна, я же предложу ровно две: статические и динамические. Именно эти ровно потому, что для них есть принципиальное различие в плане реализации.

Статические

К статическим относим фичи, о состоянии, на которое они опираются, известно заранее. Например нам надо что-то отображать в зависимости от размера экрана, или от версии операционной системы, или от начального конфига приложения. Во всех случаях к моменту создания модуля уже известно состояние, или оно меняется снаружи и сам модуль выступает в пассивной роли. Далее можно действовать двумя способами: передаем снаружи некий конфиг, или правильно настраиваем зависимости(например подставляем in-Memory хранилище вместо CoreData). В этом случае класс получается самодостаточным и ему никто не нужен для определения собственного поведения.

Динамические

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

Примеры

И наконец примеры! Давайте разберемся как это все будет выглядить на реальных примерах. Отдельно напишу, что подготовил для вас репозиторий со всем кодом, который был использован в примерах.

Для простоты понимания и чтобы абстрагироваться от какой либо конкретной архитектуры, буду использовать MVC + FeatureService, для принятия решения о включенности динамических фич. Вот теперь точно поехали!

Статические

Пример 1 (Передача конфига)

Как будет выглядить ветвление логики внутри:

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

Пример 2 (Настройка зависимостей)

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

Мы просим данные у провайдера статей и у провайдера новостей, если второго нет, мы просто ничего не получим, все довольно просто:

Как и в первом случае место управления фичей находится за пределом модуля, да еще в случае с зависимостями не требуются ни проверки, ни места ветвления, все работает само.

Пример 3 (Сервис настроек)

Представим, что при заходе на экран нам надо показывать полноэкранную рекламу, но не чаще раза в сутки.

FeatureServiceViewController.m:

Контроллер обращается к специальному сервису за этими знаниями и действует в соответствии с ответом. Как выглядит сервис внутри:

FeatureServiceImplementation.m

Сервис содержит в себе все необходимые зависиомти для принятия решения. Иногда ему также требуется передавать некоторые параметры из контроллера.

Еще больше подробностей

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

Отсутствие логики принятия решения в VC

Место ветвления

Есть несколько моментов от которых хотелось бы предостеречь

Избыточная инкапсуляция

Данный подход может привести к желанию использовать его всегда, но не стоит делать каждую фичу выключаемой:

Зависимые фичи

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

Feature toggling что это. Смотреть фото Feature toggling что это. Смотреть картинку Feature toggling что это. Картинка про Feature toggling что это. Фото Feature toggling что это

Что получаем?

Итоги

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

Напишите мне в комментариях или мне в twitter, что думаете. Буду признателен за фидбек.

Источник

Что такое feature toggle или как избавиться от мучительных мёржей и долгоживущих веток?

Проблема

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

Использование feature switcher-ов для решения проблем

Такая проблема встречается в разработке довольно часто и есть изящное решение, позволяющее взять лучшее от описанных выше подходов — feature toggle или feature switcher.

По сути, feature switcher — это boolean флаг, который хранится в базе данных и содержит информацию о том, должна быть включена та или иная фича или нет. Значение этого флага может быть извлечено из базы данных по ключу. Удобство использования feature switcher-ов заключается в том, что они могут быть легко изменены бизнес-пользователем во время runtime через панель администратора без необходимости заново деплоить приложение.

Ниже приведен пример использования feature toggle на языке Java:

В примере выше configurationManager — это класс, позволяющий извлечь значение определенного feature switcher-а из базы данных по его ключу.

Также, при помощи feature switcher-ов, можно отображать/скрывать определенные элементы на фронтенде. Для этого придется положить значение флага в Model и передать его на View как это показано ниже:

После чего использовать переданное значение для рендеринга того или иного HTML кода:

Виды feature switcher-ов

Описанный концепт использования feature switcher-ов — это лишь один возможный случай использования и такие feature switcher-ы называются release toggles. Всего выделяют 3 разных вида feature switcher-ов:

Feature toggling что это. Смотреть фото Feature toggling что это. Смотреть картинку Feature toggling что это. Картинка про Feature toggling что это. Фото Feature toggling что это

Проблемы использования feature toggle-ов

Поскольку я работаю на проекте, где активно используются feature toggle-ы, то кроме очевидных достоинств их использования я начал замечать и проблемы, связанные с ними:

Решения некоторых из описанных проблем

Помочь решить вышеописанные проблемы могут следующие действия:

Итоги

Feature switcher — очень простой и одновременно мощный механизм, позволяющий избегать монструозных коммитов, легко менять поведения приложения или собирать несколько разных приложений на одной кодовой базе, используя разную конфигурацию feature toggle-ов.

Однако, стоит также помнить, что этот паттерн разработки имеет некоторые недостатки, которые выливаются в трудночитаемый и трудно поддерживаемый код, поэтому следует избегать чрезмерного использования этого паттерна и периодически проводить документирование feature switcher-ов и их ревизию, чтобы удалять неиспользуемые и, как следствие, очищать проект от “мёртвого” кода.

Источник

Ротация функций на практике (feature toggling) 22 января 2013, 17:40

Один из самых распространенных способов параллельной (конкурентной) разработки нескольких функций – применение при контроле версий самостоятельных функциональных веток. В каждой из таких веток конкретная функция разрабатывается отдельно от других, а потом интегрируется в основной рабочий поток, когда разработка данной функции будет завершена. Такой паттерн называется «Feature Branch» (ветвление функций), см. тут

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

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

Если специалисты, занятые разработкой функциональной ветки, будут делать рефакторинг прямо в ней, это будет лишь осложнять будущие слияния и приводить к ситуациям, когда на интеграцию ветки уходит больше времени, чем на повторную разработку всего ее функционала с нуля. В результате, возникает нежелание вносить изменения, чтобы не провоцировать сложных конфликтов слияния (merge conflict). Рефакторинг необходим, но он откладывается или вообще отменяется. Вас заваливает технический долг.

Другой подход, который в последнее время активно обсуждается – разрабатывать в основной линии сразу все функции приложения. Такой подход известен под названием «стволовая разработка» (trunk-based development), подробнее см. тут. Этот подход очень хорош, если работа над всеми незавершенными функциями может быть закончена до даты следующего релиза. Но если завершить разработку всех функций до ближайшего релиза невозможно, то незавершенные функции приходится отключать – а значит, они будут невидимы для пользователей. Здесь мы подходим к обсуждения паттерна, который называется «Ротация функций» (Feature Toggle). См. тут.

Простая ротация функций

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

Что-то подобное для обеспечения простых изменений можно сделать и в логике приложения.

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

Поддержка ротации функций

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

Так, добавим точку расширения, которую можно эффективно использовать при помощи наследования.

Ту же задачу можно решить и при помощи композиции.

Внедрение зависимостей

Теперь мы можем задействовать наш контейнер для внедрения зависимостей (см. http://martinfowler.com/articles/injection.html), чтобы выполнить с его помощью большую часть требуемой в нашем случае функционально-специфичной конфигурации. Рассмотрим, как это можно сделать в Spring MVC.

Один вариант заключается в том, чтобы выделить в отдельную категорию файлы applicationContext-$.xml с определениями функционально-специфичных управляемых компонентов (бинов). Но в некоторых ситуациях нам придется иметь дело со списками таких компонентов – например, со списками перехватчиков. Если продублировать такой список в функционально-специфичном контекстном файле, то возникнут сложности с поддержкой кода.

Лучше оставить такой список в базовом файле applicationContext.xml. Если понадобится добавить к функции перехватчик, то мы сможем занести данную функцию в основной список, определить компонент в функционально-специфичном контекстном файле, а потом определить нулевую реализацию (см. wiki) в основном контекстном файле.

Аннотации

Мы также можем задействовать аннотации-маркеры (они же — аннотации без элементов) для наших функций. Таким образом, нам не придется повторяться в XML-файлах. Мы будем использовать значение аннотации для указания того, нужен ли нам аннотированный компонент при включенной или при отключенной функции.

Еще нам понадобится создать специальный (custom) фильтр TypeFilter, который будет использовать информацию о переключении функций и нашу аннотацию. Все это делается для включения в код корректной реализации…

… после чего добавим наш фильтр типов в конфигурацию сканера spring-компонентов.

Теперь можем продолжить и правильно аннотировать наши реализации, а остальную работу за нас выполнит Spring

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

Отделение статических ресурсов

В предыдущем разделе мы рассмотрели несколько способов переключения функциональных веток на серверной стороне. Но что делать со статическими ресурсами, такими, как JavaScript и CSS?

Некоторые статические ресурсы можно превратить в шаблоны, отображаемые на серверной стороне. Так мы можем добавить логику для изменения их специфичным для конкретных функций образом. Но при таком подходе статические ресурсы, которые можно было бы разместить в сети доставки контента (Content Delivery Network, см. wiki), переносятся обратно на сервер приложений, что может быть неприемлемо в некоторых ситуациях.

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

Гораздо рациональнее было бы оставить статические ресурсы в виде статических файлов и создавать функционально-специфичные версии статического контента. Эти версии мы сможем в условном порядке подключать из наших динамических шаблонов. Так, можно взять shopping_cart.css и создать файл shopping_cart_foo.css, в котором мы поместим специфическое оформление для корзины покупок, применяемое при включенной функции Foo.

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

Предотвращение случайного раскрытия

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

Легко предположить, что утечка информации о недоработанных функциях может произойти из-за того, что функция недостаточно хорошо «обернута» в механизм ротации. Если вам повезет, такая утечка вскроется в виде какой-нибудь очевидной ошибки. Но чаще ошибка проявляется гораздо менее явно: в каком-то сообщении окажется неверный текст, отобразится дополнительное поле и т.д.

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

Самый неявный канал, через который возможна утечка еще не выпущенных функций – это потенциально невидимые артефакты. Ими могут быть неиспользуемые классы CSS, код JavaScript и даже комментарии в HTML. Случайное раскрытие такого рода очень сложно заметить.

Так или иначе, самый надежный способ избежать утечек – соблюдать аккуратность и дисциплину при разработке. Любые наработки по определенной функции должны обертываться в код для ротации функций. Изменения статических ресурсов необходимо выполнять в отдельных функционально-специфичных файлах, эти файлы должны быть обернуты в код для ротации функций. Если всегда отделять функционально-специфичные статические файлы, то мы сможем даже не включать их в разработку – просто для обеспечения безопасности.

Ротация функций во время исполнения

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

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

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

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

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

Мы также можем предоставить специальный управляющий интерфейс. Примером такого интерфейса является JMX (см. wiki). В таком случае, все изменения можно будет применять непосредственно к работающему серверу приложений. В таком случае, мы, конечно, сможем осуществлять изменения мгновенно, но потребуется обеспечить дополнительную координацию, обеспечивающую взаимно непротиворечивое добавление функций на всем множестве серверов. Если мы собираемся применять изменения непосредственно к работающему серверу приложений, то необходимо позаботиться и о том, чтобы такое переключение оставалось в силе и после перезапуска приложения.

Ротация во время сборки

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

Несовместимые зависимости

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

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

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

Тестирование ротации функций

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

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

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

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

Но, в отличие от ситуации с отдельными ветками, при вышеописанной постановке задачи возможна ситуация, в которой сборка для функции A не позволяет пройти тест или выполнить сборку для функции B. Честно говоря, эта проблема сохраняется и в случае с отдельными ветками. Однако, при работе в отдельных ветках мы откладываем данную проблему вплоть до окончательной интеграции. Ротация функций позволяет обнаруживать подобные проблемы при каждой отправке кода, что, на мой взгляд, более предпочтительно.

Удаление переключений в готовых функциях

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

Чтобы было проще избавляться от уже не нужных переключателей, их можно сделать статически типизированными. Это означает, что у нас должен быть метод FeatureToggles.isFooEnabled(), а не FeatureToggles.isFooEnabled(“foo”). Таким образом, мы сможем задействовать компилятор для простого удаления ненужных переключателей из кода. Ведь как только мы удалим метод isFooEnabled(), любой код, по-прежнему использующий его, не скомпилируется.

Кроме того, мы сможем задействовать нашу интегрированную среду разработки (IDE) для поиска примеров использования заданного метода и, соответственно, нахождения мест, где этот метод также должен быть удален из шаблонов – если наша IDE поддерживает такие операции.

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

Заключение

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

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

Читайте также

В продолжение прошлой статьи рассмотрим пример настройки ротации логов контейнеров на примере CentOs 7. В моём случае stdout и stderr контейнеров…

Краткое саммари заметок про пользовательскую историю, она же юзерстори. Игры дают опыт, в том числе опыт эмоций. Опыт и эмоции…

Допустим, вы хотите разработать новую фичу, но не уверены, что она понравится пользователям, и вам нужно иметь способ безболезненно её…

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *