Флаги для функций
Флаги для функций (feature Flags) это проверенный временем способ, позволяющий контролировать возможности приложения или сервиса самым решительным образом.
Пример
Допустим, у вас есть приложение или сервис, который запускается из командной строки, и у него есть main
метод или функция. Вашим флагом функции может быть --withOneClickPurchase
, который вы отправляете в качестве аргумента в командную строку. Этот флаг активирует те строчки кода в приложении, которые реализуют функцию покупки в один клик от Amazon. Без этого аргумента командной строки приложение будет отображать корзину с товарами. Не будем вдаваться в подробности - допустим что разработчики решили сделать именно такую логику приложения. Скорей всего, ‘покупка в один клин’ и ‘корзина с товарами’ на языке бизнеса будет называться точно так же. Все усложняется тем, что эти флаги не обязательно должно быть реализованы как a/b тест или новый/старый функционал - эти функции могут дополнять или подменять друг друга. В нашем примере, может быть нужна возможность сделать --allowUsersToUseShoppingCartInsteadOfOneClick
. Как вы видите, флаги могут подменять друг друга.
Флаги это Переключатели
Светило индустрии, Мартин Фаулер, называет эту технику ‘переключатели функций’, и дал им фундаментальное определение (см.ссылки ниже). Тем не менее, в индустрии чаще фигурирует название флаги функций (feature flags), поэтому далее мы будем использовать именно это название.Атомарность
Может быть и так, что один флаг управляет чем-то большим, например пользовательским интерфейсом какого-то компонента. В примере выше мы определили, что OneClickPurchasing
и ShoppingCart
это названия компонентов. Возможно, степень атомарности флага должна быть меньше. Допустим, американцы хотят видеть температуру в фаренгейтах, а другие народы в градусах цельсия. У нас может быть флаг --temp=F
и --temp=C
. Разработчики для прикола могут также добавить температуру в кельвинах --temp=K
.
Реализация
Для OneClickPurchasing
и ShoppingCart
вариантов может быть создана абстракция PurchasingCompleting
. Затем на начальном этапе загрузки, который контролируется кодом, флаг --withOneClickPurchase
будет влиять на:
Java, вручную:
if args.contains("--withOneClickPurchase") {
purchasingCompleting = new OneClickPurchasing();
}
Внедрение зависимости в Java через настройку:
bootContainer.addComponent(classFromName(config.get("purchasingCompleting")));
В общем, есть много путей передать флаг (или любой другой конфиг) в среду выполнения. Если вы можете, то вам лучше избегать if/else конструкций в коде в том месте, где происходит выбор того, какую часть кода делать активной. Отсюда наш акцент на абстракцию.
Процесс Continuous Integration
Важно, чтобы CI был бдительным при всех ваших ощутимых и ожидаемых изменениях флагов. Это означает, что тесты, которые прогоняются в приложении или сервисе после запуска, должны адаптироваться и проверять то, на что влияют изменения флагов. Это также означает, что с точки зрения процессов CI существует разветвление после unit тестов для каждого ощутимого изменения флага. Грубый пример - запускать весь процесс CI параллельно для каждого ощутимого изменения флагов. Это будет означать, что каждое вливание кода в trunk запускает более одной сборки - надеюсь, из гибкой инфраструктуры.
Переключаемое состояние во время выполнения программы
Иногда флагов, заданных при запуске приложения, недостаточно. Допустим, вы авиакомпания, продающая билеты на рейсы через Интернет. Вы также можете сдавать автомобили в аренду в сотрудничестве с партнером, скажем, «Really Cool Rental Cars» (RCRC). Подключение к любому партнеру или проверка текущего статуса его системы находится вне вашего контроля, поэтому вам может потребоваться переключатель в программном обеспечении, который работает без перезапуска, чтобы включить или выключить “резервирование партнеров RCRC” и дать возможность группе поддержки переключить его, если определенные условия “Runbook” были соблюдены. В этом случае конечные пользователи могут не заметить, все ли Hertz, Avis, Enterprise и т.д. (сервисы проката автомобилей - прим. пер.) находятся в предложениях для этого аэропорта во время прибытия рейса.
Ключевым моментом для переключаемых флагов во время выполнения является необходимость сохранения состояния. При перезапуске приложения или службы не следует возвращать этот флаг по умолчанию - он должен сохранить предыдущее значение. Все усложняется когда вам нужно чтобы флаг пронизывал несколько узлов в кластере горизонтально масштабируемых родственных процессов. Для этого случая существует современный способ фиксации состояние флага в Consul, Etcd
или его эквиваленте.
Флаги сборки
Флаги сборки влияют на приложение или службу в процессе ее сборки. Что касается флага --withOneClickPurchase
, то приложение не сможет во время выполнения иметь эту возможность, если каким-то образом во время сборки подходящий флаг не был задан.
A/B тестирование и бета версии
Отправка кода, который выключен в рабочей среде, позволяет вам включить его по разным причинам - например, вы хотите чтобы какая-то часть пользователей явно или не явно проверила его. A/B тестирование (которое так любит маркетинг) возможно с флагами, которые могут менчть свое значение во время работы программы. Таким образом, есть бета версии функционала/отдельных функций, которые доступных для определенных групп пользователей.
Технический долг - это ловушка
Флаги со временем копятся в кодовые базы и часто забываются, когда команды разработчиков стремятся к новым бизнес-результатам. Конечно, вы хотите подождать некоторое время, пока не станет ясно, что вы поняли какое именно состояние флага вам подходит лучше всего, и именно в этом проблема - приложение отлично работает с флагом, оставленным на месте, и бизнес действительно заботится только о новых приоритетах. Единственное спасение - это то, что у вас были unit тесты для всего, даже для кода, который фактически отключен в производственной среде. Постарайтесь договориться с бизнесом разрешить удаление флагов (и кода, к которому они применяются) через месяц после выпуска. Может быть, добавить их в README проекта с датой «проверки на удаление».
История
Некоторые исторические предшественники переключателей/флагов функций, какими мы их знаем сегодня:
- нифицированное управление версиями с помощью логики функций (Andreas Zeller and Gregor Snelting, 1996)
- официальная публикация.
- Управление конфигурацией с наборами версий: унифицированная модель управления версиями программного обеспечения и ее приложений (Andreas Zeller’s, 1997)
- Ph.D. тезис.
Также есть предупреждение:
Бред Эплтон (Brad Appleton) сказал:
“ Что мне не нравится в переключателях функций/флагах, так это то, что они НЕ являются недолговечными, как предполагалось, и нам приходится возвращаться к знаменитой статье Спенсера и Коллайера. Самое смешное, что ветки для функций (feature-branches) появаились точно таким же путем. Когда они были впервые представлены, это предназначалось для функциональных команд, разрабатывающих очень большие функции, а отдельные ветки для функций были нужны потому, что слишком много людей пытались одновременно влить код в одну и ту же ветку. Таким образом, идея заключалась в использовании отдельных ветвей (для масштабирования), и команды могли бы интегрироваться в свою командную ветвь ежедневно или чаще с интеграцией по крайней мере каждую ночь по всем функциональным веткам [вздох].
Внешние ссылки
show references