Советы

Фича-флаги в .NET: от простого к сложному

Фича-флаги в .NET: от простого к сложному

Статья представляет собой краткий обзор-объяснение того, как наша команда работает с флагами функций (они же фича-флаги, англ. feature flags, сокр. FF). Здесь мы рассмотрим, как добавлять их в код и тесты, а потом удалять самым простейшим образом во избежание ошибок. 

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

Применение FF проиллюстрируем на примерах упрощенного кода (в реальном мире мы проходим ряд промежуточных этапов из-за использования TDD).

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

Например, изменим фильтр в одном из действий, который только извлекает информацию из базы данных. Поехали!

Действие выглядит так: 

class RetrieveStuff: def execute(self, name: str) -> Stuff:

return Stuff.objects.filter(name=name).first()

Сначала меняем имя, присоединяя суффикс Old, после чего добавляем новое действие: 

class RetrieveStuffOld: def execute(self, name: str) -> Stuff: return Stuff.objects.filter(name=name).first()class RetrieveStuff: def execute(self, name: str) -> Stuff:

return Stuff.objects.filter(name=name, is_active=True).first()

Применяем тот же подход, что и в случае с действием. Изначально у нас есть тесты (с уже измененным именем): 

class TestRetrieveStuffOld: def test_retrieve_stuff_by_name(self): Stuff.objects.create(name=»a_name») stuff: Stuff = RetrieveStuffOld().execute(«a_name»)

assert stuff.name == «a_name»

Теперь мы дублируем тесты для нового действия и вносим необходимые изменения: 

class TestRetrieveStuffOld: def test_retrieve_stuff_by_name(self): Stuff.objects.create(name=»a_name», is_active=False) stuff: Stuff = RetrieveStuffOld().execute(«a_name») assert stuff.name == «a_name»class TestRetrieveStuff: def test_retrieve_stuff_by_name_and_active(self): Stuff.objects.create(name=»a_name», is_active=True) stuff: Stuff = RetrieveStuff().execute(«a_name») assert stuff.name == «a_name»

assert stuff.is_active

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

class RetrieveStuffView(APIView): def get(self, name: str): stuff: Stuff = RetrieveStuffOld().execute(name)

return Response(data=StuffSerializer(stuff).data)

На этом этапе мы обычно создаем пул-реквест и и развертываем его в продакшн. 

Поэтому самое время добавить FF в представление:

class RetrieveStuffView(APIView): def get(self, name: str): if not flags.is_active(«use-new-filter-for-stuff»): stuff: Stuff = RetrieveStuffOld().execute(name) return Response(data=StuffSerializer(stuff).data) stuff: Stuff = RetrieveStuff().execute(name)

return Response(data=StuffSerializer(stuff).data)

Обратите внимание, что FF добавляется с инструкцией not. Впоследствии этот прием позволит легко его удалить. Но об этом чуть позже. 

Действуем практически так же, как и в случае с тестами для действия. Мы дублируем имеющиеся тесты для представления и добавляем фикстуру fixture, чтобы включать или выключать FF для каждого тестового класса: 

class TestRetrieveStuffViewWhenFlagDisabled: @pytest.fixture(autouse=True) def manage_flag(self, activate_flag): activate_flag(«use-new-filter-for-stuff», False) def test_retrieve_stuff_by_name(self, client): response = client.get(«/api/stuff/a_name/») assert response.status_code == 200 assert response.json() == { «name»: «a_name», «is_active»: False, }class TestRetrieveStuffView: @pytest.fixture(autouse=True) def manage_flag(self, activate_flag): activate_flag(«use-new-filter-for-stuff», True) def test_retrieve_stuff_by_name_and_active(self, client): response = client.get(«/api/stuff/a_name/») assert response.status_code == 200 assert response.json() == { «name»: «a_name», «is_active»: True,

}

Отлично! Теперь мы можем сделать еще один пул-реквест с этим кодом, создать и активировать новый FF в системе и начать использовать новую функциональность в среде продакшн. 

Переходим к самому интересному этапу. Как правило, мы удаляем FF через 2 дня после развертывания нового кода в продакшн (или после того, как посчитаем, что новый код прошел 100% проверку).

Благодаря тому, что все было продублировано, мы можем без колебаний удалить FF и старый код. Вот так просто: 

Фича-флаги в .NET: от простого к сложномуПредставление
Фича-флаги в .NET: от простого к сложномуДействие 
Фича-флаги в .NET: от простого к сложномуТесты для представления 
Фича-флаги в .NET: от простого к сложномуТесты для действия 

Всего лишь нужно удалить все с суффиксом Old или WhenFlagDisabled, не задумываясь о всяких “если”, тестах и сложных способах действий.  

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

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

Намного лучше, чем вставлять флаги и смешивать их с кодом продакшн. Советую попробовать  —  за загрузку дополнительных строк кода в репозиторий денег не берут!

  • Читайте нас в Telegram, VK и Дзен
  • Перевод статьи Felipe Álvarez: How we manage Feature Flags ????

Фича-тогглы: инструкция по применению

Всем привет! Я Павел, тимлид команды SLA, и занимаюсь оценкой надёжности Авито. В своей прошлой статье я рассказал про стратегии ветвления и Trunk Based Development. Если не читали, переходите по ссылке. А сейчас я хочу рассказать про фича-флаги, которые появляются именно в контексте TBD. 

Фича-флаги в .NET: от простого к сложному

Что такое фича-флаг

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

Самый простой вариант: if (false):

func (s *Service) Handle {

// мы не хотим, чтобы фича работала пока не готова
// @see is in active development
if (false) {
// Добавим функционал со списком покупок, но список всегда пустой
result[«purchases»] = []Purchase{}
}

}

Согласно TBD-подходу, каждую итерацию фича расширяется и обрастает «мясом»:

func (s *Service) Handle {

// мы не хотим, чтобы фича работала пока не готова
// @see is in active development
if (false) {
// делаем запрос в сервис, за списокм покупок
purchases := s.purchasesRepository.GetLast(limit, userID)
// Добавим список покупок к ответу
result[«purchases»] = purchases
}

}

И в момент, например, когда фича частично или полностью работоспособна, мы меняем false на динамический фича-флаг:

const PurchaseListFeature = «some-string»
func (s *Service) Handle {

// проверим, включена ли фича
if (s.FeatureFlags.IsEnable(PurchaseListFeature)) {
// делаем запрос в сервис, за списокм покупок
purchases := s.purchasesRepository.GetLast(limit, userID)
// Добавим список покупок к ответу
result[«purchases»] = purchases
}

}

Теперь мы можем динамически включать и выключать фичу. Например, для проведения регрессионных тестов или экстренного выключения, если рискованный функционал начнет «стрелять». Но сами тогглы могут применяться и в отрыве от TBD-процессов.

Типы тогглов

Мартин Фаулер выделяет 4 типа тогглов, исходя из срока их жизни и частоты изменений:

  1. Release Toggles: самые важные тогглы. Они нужны для включения фичей, которые сделаны в TBD-подходе или не прошли процесс регрессионного тестирования.

  2. Ops Toggles: закрывают функциональные блоки, которые описывают Ops-составляющую. Например включение и выключение режима осады, переключение типа капчи, переключение нагрузки с одной базы на другую.

  3. Permission Toggles: тогглы для определённых групп пользователей, которым включается новый функционал. Например для сотрудников или тестировщиков.

  4. Experiment Toggles: тогглы для A/B-тестов.

Фича-флаги в .NET: от простого к сложномуТипы тогглов по Мартину ФаулеруВажно!

Типы тогглов по Мартину Фаулеру — это не изолированные категории. Ops-тоггл можно совместить, например, с Experiment-тогглом.

Способы реализации динамических тогглов

Bool-toggles — простые тогглы-константы. 

Константы можно зашивать прямо в код, положить в конфиг, или даже класть в RedisBD и управлять ими из админки. В них важна простота и только два стейта: truefalse.

Они хорошо подходят для Realease-тогглов и для Ops-тогглов:

type Toggles struct {
constToggles map[string]bool
}

func (t *Toggles) IsEnable(toggleName string) bool {
return t.constToggles[toggleName]
}


if (toggleService.IsEnable(Feature)) {
// здесь описываем функционал под тогглом
}

Percent Toggles включаются с некоторой вероятностью. Удобны как Ops-тогглы для плавной раскатки фичи и регулирования нагрузки. Например, с их помощью мы проверяем запрос через усиленную антибот-систему или семплируем трафик, метрики, трейсы.

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

type Toggles struct
percentToggles map[string]int
}

func (t *Toggles) IsEnable(toggleName string) bool {
// значение в диапазоне [0, 100]
percent := max(0, min(100, t.percentToggles[toggleName]))
return percent >= rand.Intn(100)+1
}


if (toggleService.IsEnable(Feature)) {
// здесь описываем функционал под тогглом
}

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

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

Idempotent Percent Toggles такие же, как процентные тогглы. Но их поведение не меняется с обновлением страницы для одного пользователя. Они подходят для Release-, Ops-, Experiment-тогглов.

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

type Toggles struct
idempotentPercentToggles map[string]int
}

func (t *Toggles) IsEnable(toggleName string) bool {
// значение в диапазоне [0, 100]
percent := max(0, min(100, t.idempotentPercentToggles[toggleName]))
// cчитаем md5 от критерия
// превращаем в число
// считаем остаток от деления на 100
// ВАЖНО — что для одного и того-же критерия тоггл имеет одно и то-же значение
return percent >= t.getMd5Mod100(criteria)
}


if (toggleService.IsEnable(Feature)) {
// здесь описываем функционал под тогглом
}

Читайте также:  Как сформировать новую привычку и избавиться от старой. Завести полезную привычку

Во всех этих типах важно использовать устойчивый критерий, который будет редко меняться. Им могут стать DeviceID, Fingerprint, UserID. Если критерий неустойчивый, то «кубики» будут бросаться каждый раз, и каждый раз поведение для пользователя будет определяться заново.

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

Фича-флаги в .NET: от простого к сложномуФича статистики закрыта обычным процентным тогглом

Тогглы в микросервисах

«Куда выносить тогглы?» — частый вопрос, особенно при распиле монолита. Часто бывает, что один тоггл определяет поведение, которое выносится в несколько разных сервисов. К примеру, тоггл «показать статистику» есть и в сервисе для мобильных приложений, и в сервисе для десктопа. Мы в Авито много думали над этим вопросом.

И вариантов тут всего два:

Фича-флаги в .NET: от простого к сложному

Синхронные тогглы

Первая идея, которая приходит в голову — сделать сервис тогглов и вынести их туда. Тогда микросервисы будут получать состояние тогглов из него. 

Это не лучший выбор, потому что:

  • нет явной связи между сервисами;
  • может получиться асинхронный монолит, а не микросервисы;
  • придётся ждать релиза всех сервисов, где есть тоггл, прежде чем его включать;
  • включение и выключение — всегда огромный риск: не предугадаешь, где и что бомбанёт.

Отдельные тогглы на каждый микросервис

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

Этот подход работает лучше, потому что: 

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

A/B-тесты и Experiment-toggles

Тогглы для проведения A/B-тестов мы обычно реализуем отдельно. По своему поведению они похожи на процентные идемпотентные.

Важно использовать разные механизмы для обычных тогглов и A/B-тестов, потому что A/B-тогглы:

  • всегда обкладываются тонной избыточной аналитики, в отличие от обычных;
  • имеют множество настроек, например: «только платящим», «только для Android»;
  • должны иметь отдельный интерфейс для работы аналитиков и продактов.

Если использовать один и тот же механизм:

  • вы никогда не отделите просто тогглы от A/B,
  • будет сложнее удалять устаревший код,
  • будут постоянные риски создать ОГРОМНУЮ и лишнюю нагрузку на системы аналитики. Например, процентный тоггл легко раскатить на 100%, и начать слать вагон аналитики в 100% случаев вместо каноничных 2%.

Проблемы в использовании фича-тогглов

У тогглов есть не только плюсы, есть и ворох проблем. Но если знать про них, знать подходы к решению, то последствия легко минимизировать.

Фича-флаги в .NET: от простого к сложному

Накопление тогглов

Со временем количество тогглов может стать просто огромным! Они будут встречаться в самых разных кусочках приложения. Я как-то видел тоггл, который был не актуален более 5 лет, но до сих пор существовал.

Последствия:

  • сложно проводить рефакторинг;
  • будут появляться вложенные друг в друга тогглы;
  • ошибки после включения или отключения не того тоггла: из-за схожих названий, ошибочных описаний.

Решение: нужно перестраивать процессы работы в компании. Появление любого нового тоггла должно приводить и к появлению задачи на его удаление.

Тестирование

У вас же есть тесты, да? А в каком состоянии тогглов вы тестируете? А все состояния тогглов вы проверяете? Привет комбинаторному взрыву 🙂

Последствия:

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

Решение: dev-среда должна синхронизироваться с продом. Самые важные тогглы могут иметь отдельные автотесты, проверяющие их поведение во включенном и выключенном состоянии.

Тогглы неизвестного происхождения

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

Последствия:

  • старые тогглы: неясно, зачем они нужны;
  • новые тогглы: неясно, как они работают.

Решение —в интерфейсе управления тогглами нужно отражать весь цикл жизни тоггла: 

  • задача, в которой он добавлен;
  • задача в которой он будет удалён;
  • описание;
  • владелец.

Выводы

Тогглы в концепции фича-флагов необходимы для быстрой скорости Delivery и Trunk Based Development. В современной разработке с её гибкими практиками без этого не обойтись. Это позволяет нам реализовывать TBD-подходы, плавно управлять нагрузкой. А ещё быть гибче, смелее катить новые фичи, зная что они закрыты тогглом. 

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

Битовые маски и флаги в .NET C# | DEV

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

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

К сожалению, всё чаще наблюдаю, что много современных программистов из «нового поколения» не работают с данным типом представлений вообще и, что ещё хуже, практически не знакомы или не слышали об этой идее.

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

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

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

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

К примеру, для десятичного числа 91 ряд будет выглядеть примерно следующим образом (рисовал на планшете, авторское исполнение ???? но, я уверен, в этом есть некоторая определённая прелесть):

Фича-флаги в .NET: от простого к сложному

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

Возьмём, к примеру классическую задачу организации доступа юзера и примем, что у нас на сайте есть абстрактные группы, в которых есть следующие виды «допусков»: чтение статей, создание статей, удаление статей, чтение комментариев, написание комментариев, удаление комментариев, создание юзеров и блокировка юзеров.

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

Фича-флаги в .NET: от простого к сложному

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

Подобное множество запрограммировать «просто и быстро» — не просто и не быстро ???? Но, если применить битовые маски, где, скажем, 4й бит это С, 3й бит — М, 2й — Y и 1й — K, всё становится на порядок легче и проще! Можно передавать всевозможные комбинации используя всего-лишь один байт (и то, займём там только половину «полезной площади»).

Таблица истинности

Итого, мы умеем переводить числа в двоичное представление. Теперь необходимо вспомнить таблицу истинности (хотя, конечно, это не про «вспомнить», а про элементарную логику, которая обычно выводится — ничего запоминать не нужно).

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

Всё это в сокращённом виде (для базовых функций AND, OR, XOR) выглядит так:

A B AND OR XOR
0 0 0 0 0
1 0 0 1 1
0 1 0 1 1
1 1 1 1 0

Таблица истинности

Логические операторы

Как правило все вышеописанные логические функции представлены во всех языках программирования в том или ином виде и конкретно для «си-подобной» группы языков это, как правило, абсолютно всегда одинаковый синтаксис. Вы встретите это в C, С++, C#, PHP, JavaScript, Python и ещё много где. Это всё в некотором роде уже стало стандартом синтаксиса для битовых (bitwise) операций.

Оператор & для AND

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

Читайте также:  Большие языковые модели (LLM): что это такое и как они работают?

Разберём пример — для строчки кода 111 & 222 ответ будет 78, т.к. 111 в десятичной системе это 01101111 в двоичной, а 222 — 11011110. Проходя каждый элемент и применяя поочерёдно логическую операцию AND мы получим 01001110 (единица там и только там, где в исходных битах обоих операндов были единицы), что является 78 в десятичной системе.

Вот ещё несколько примеров логического И (биты, которые «отработали» положительно я выделил жирным):

decbinANDdecbinResultdecbin
111 01101111 & 222 11011110 = 78 01001110
43 00101011 & 96 01100000 = 32 00100000
131 10000011 & 87 01010111 = 3 00000011
47 00101111 & 255 11111111 = 47 00101111

Примеры конъюнкции

Оператор | для OR

Логическое ИЛИ или же дизъюнкция. Здесь, в отличии от AND, нам для «положительного» результата необходимо, чтобы хотя бы один из операндов равнялся единицы.

Без разницы левый, правый или оба — возвращаем 1, в противном случае — 0.

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

Разберём пример (я буду рассматривать такие же числа во всех примерах, чтобы можно было понятьуловить разницу). 111 | 222 будет равняться 255, т.к. 01101111 ИЛИ 11011110 вернут все единицы 11111111 (у нас нет ни одного случая при котором было бы два ноля).

Также, как и в предыдущем примере, добавляю таблицу примером логического ИЛИ:

decbinORdecbinResultdecbin
111 01101111 | 222 11011110 = 255 11111111
43 00101011 | 96 01100000 = 107 01101011
131 10000011 | 87 01010111 = 215 11010111
47 00101111 | 255 11111111 = 255 11111111

Примеры дизъюнкции

Оператор ^ для XOR

Исключающее ИЛИ или строгая дизъюнкция — моя любимая логическая операция, если, конечно, можно испытывать чувства к этому.

Также известная как XOR или сложение по модулю 2, поразрядное дополнение, инвертирование по маске, жегалкинское сложение, логическое вычитание или логическая неравнозначность.

Эта прекрасная операция вернёт вам единицу или true в том случае, если строго один (любой, но только один) операнд равен нулю.

Для нашего примера из чисел 111 ^ 222 ответом будет 177, потому что 01101111 XOR 11011110 даст 10110001.

decbinXORdecbinResultdecbin
111 01101111 ^ 222 11011110 = 177 10110001
43 00101011 ^ 96 01100000 = 75 01001011
131 10000011 ^ 87 01010111 = 212 11010100
47 00101111 ^ 255 11111111 = 208 11010000

Примеры строгой дизъюнкции

Оператор битового смещения (сдвига) влево 1 — получим 12, а в случае 24 >> 2 — получим 6.

Оператор ~ для NOT

Самый простой оператор ~, работающий с одним операндом (т.е. унарная операция) — это логическое НЕ, которое инвертирует наши входные данные на противоположные (true на false и наоборот), т.е. если есть 1 — то возвращаем 0 и, соответственно, если 0 — то 1. Здесь без таблиц, всё более менее должно быть очевидно.

Есть только один ньюанс — инвертирование изменяет также знак числа. К примеру ~123 станет -124, т.к. первый разряд битового представления отвечает за знак числа — если там установлена единица, значит число отрицательное.

Таким образом мы можем умножать числа на -1 путём инвертирования и прибавления 1: ~123+1.

Этот способ называется дополнительный код (или two’s complement) — наиболее простой и эффективный вариант представления отрицательных чисел для компьютера.

Работа с битами

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

Но перед тем, как мы начнём производить манипуляции над битами, хочу напомнить вам ещё кое что — каждый разряд бита в двоичном представлении это ряд степени двойки, т.е. 2^n.

Посмотрите, как единичка «двигается» по диагонали в зависимости от степени — именно так мы и будем «вычленять» или, наоборот, добавлять нужные нам флаги:

ndecB7B6B5B4B3B2B1B0
0 1 0 0 0 0 0 0 0 1
1 2 0 0 0 0 0 0 1 0
2 4 0 0 0 0 0 1 0 0
3 8 0 0 0 0 1 0 0 0
4 16 0 0 0 1 0 0 0 0
5 32 0 0 1 0 0 0 0 0
6 64 0 1 0 0 0 0 0 0
7 128 1 0 0 0 0 0 0 0

Теперь пример. Для того, чтобы заменить в числе 0b11110111 ноль на единицу, мы должны изменить четвёртый бит.

Для этого необходимо применить логическое ИЛИ (OR) с десятичной восьмёркой (которая и несёт в себе этот самый бит — посмотрите в табличке), т.е.

0b11110111 | 0b1000 и мы получим 255, т.е. все восемь единиц 11111111.

Для того, чтобы убрать эту же единицу (т.е. совершить обратную операцию), мы должны воспользоваться исключающим ИЛИ (XOR): 0b11111111 ^ 0b1000 = 0b11110111.

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

.NET C# пример перечисления

Давайте перенесём наш изначальный пример с ACL в код и создадим стандартый enum на языке C#:

[Flags]
public enum AccessLevels : uint
{
None = 0,

//Articles
READ_ARTICLES = 1,
WRITE_ARTICLES = 2,
DELETE_ARTICLES = 4,

//Comments
READ_COMMENTS = 8,
WRITE_COMMENTS = 16,
DELETE_COMMENTS = 32,

//Profiles
CREATE_PROFILES = 64,
BLOCK_PROFILES = 128,

ALL = ~None,
}

Данная структура позволяет нам создавать перечисления с типом uint и организовать примитивный и очень легковесный ACL (access control list), т.е. систему доступов.

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

по-умолчанию структура принимает значение 0 — это считается хорошей и правильной практикой. Последним элементом идёт ALL равный инверсией None (т.е. заполнены все биты, все возможные варианты значений).

Здесь же вы можете заметить, что все последующие элементы «привязаны» к значениям степени двойки (1, 2, 4, 8, 16, 32…) — это те самые биты, которые мы и будем «устанавливать». К слову, записывать эти значения можно разными способами:

//Left shift
[Flags]
public enum CMYK1 : uint
{
None = 0,
C = 1,
M = C this.Value |= value;

public void Remove(AccessLevels value) => this.Value ^= value;

public bool Contains(AccessLevels value) => (this.Value & value) == value;

public override string ToString() => this.Value.ToString(«G»);

}

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

Данный код вы можете найти в моём «блогерском» репозитории здесь.

Дополнительно

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

К примеру, в онлайне есть прекрасный инструмент bitwisecmd, которые позволяет проводить любые битовые операции над числами (AND, OR, XOR, битовые смещения и т.д.).

А также, вы можете посмотреть мой старый проект BitJuggling на GitHub, который позволяет делать всё то же самое, но только как стандартное приложение Windows:

Фича-флаги в .NET: от простого к сложному

Выводы

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

Флаги компонентов — .NET

Совет

Это фрагмент из электронной книги Разработка облачных приложений .NET для Azure, доступной в документации по .NET или в виде бесплатного pdf-файла, который можно читать в автономном режиме.

Фича-флаги в .NET: от простого к сложному

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

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

Они отделяют выпуск новых функций от развертывания кода.

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

Ниже приведены другие варианты использования флагов функций.

  • Ограничьте функциональность уровня «Премиум» определенными группами клиентов, желающими платить более высокую плату за подписку.
  • Стабилизируйте систему, быстро деактивируя функцию проблемы, избегая риска отката или немедленного исправления.
  • Отключите необязательную функцию с высоким потреблением ресурсов в периоды пикового использования.
  • Проводите experimental feature releases в небольших сегментах пользователей для проверки осуществимости и популярности.
Читайте также:  Proglib Academy: курсы программирования с нуля

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

Реализация флагов функций

По своей сути флаг функции является ссылкой на простой decision object. Он возвращает логическое состояние on или off. Флаг обычно заключает в оболочку блок кода, который инкапсулирует возможность функции. Состояние флага определяет, выполняется ли блок кода для заданного пользователя. На рисунке 10-11 показана реализация.

if (featureFlag) {
// Run this code block if the featureFlag value is true
} else {
// Run this code block if the featureFlag value is false
}

Рис. 10-11 . Простая реализация флага функции.

Обратите внимание, что этот подход отделяет логику принятия решений от кода функции.

В главе 1 мы обсудили Twelve-Factor App. В этом руководстве рекомендуется сохранять параметры конфигурации внешними из исполняемого кода приложения. При необходимости параметры можно считывать из внешнего источника.

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

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

Флаги функций можно легко реализовать в службе ASP.NET Core. Установка библиотек управления функциями .NET и поставщика Конфигурация приложений позволяет декларативно добавлять флаги компонентов в код. Они позволяют использовать FeatureGate атрибуты, чтобы вам не нужно было вручную писать операторы if в базе кода.

После настройки в классе Startup можно добавить функциональные возможности флагов функций на уровне контроллера, действия или ПО промежуточного слоя. На рис. 10-12 представлена реализация контроллера и действия:

[FeatureGate(MyFeatureFlags.FeatureA)]
public class ProductController : Controller
{

}

[FeatureGate(MyFeatureFlags.FeatureA)]
public IActionResult UpdateProductStatus()
{
return ObjectResult(ProductDto);
}

Рис. 10-12 . Реализация флага компонента в контроллере и действии.

Если флаг функции отключен, пользователь получит код состояния 404 (Не найдено) без текста ответа.

Флаги функций также можно внедрять непосредственно в классы C#. На рисунке 10-13 показана внедрение флага функции:

public class ProductController : Controller
{
private readonly IFeatureManager _featureManager;

public ProductController(IFeatureManager featureManager)
{
_featureManager = featureManager;
}
}

Рис. 10-13 . Внедрение флага компонента в класс.

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

Они могут гарантировать неизменяемость состояний флагов во время вызова запроса. Они также предлагают .Point-in-time snapshot Вы можете восстановить историю любого ключа-значения и указать его прошлое значение в любой момент в течение предыдущих семи дней.

Конфигурирование приложений в рантайме: фича-флаги — Техника на vc.ru

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

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

В этом контексте фича-флаги (feature flags) становятся все более популярным инструментом для конфигурирования приложений в рантайме.

{«id»:736627,»gtm»:null}

1.5K показов

342 открытия

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

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

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

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

Еще одним преимуществом фича-флагов является возможность быстрого реагирования на изменения требований и условий.

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

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

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

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

Неправильное использование фича-флагов может привести к сложноотслеживаемому и непредсказуемому поведению приложения.

Поэтому важно тщательно планировать и управлять фича-флагами, особенно в крупных и сложных проектах.

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

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

Однако, необходимо тщательно планировать и управлять фича-флагами, чтобы избежать потенциальных проблем.

Начал делать инструмент для работы с фича-флагами на C# (GitHub). Буду рад, если присоединитесь к разработке 🙂

Также в своём канале рассказываю о прогрессе проекта, а также помогаю с контрибьютингом.

How to Toggle Functionality in C# with Feature Flags

Toggling functionality using feature flags in .NET Core apps is quite straightforward, especially with a neat little feature flag management service.

In this post, I’ll walk you through how to build a simple web application using Okta for user authentication and how to use ConfigCat to manage and access feature flags.

What Are Feature Flags?

Feature flags (aka. feature toggles) are a relatively new software development technique that enables development teams to turn features on and off on the fly. You can think of them as a remote control for your application, or an actual on/off switch for a feature. You can do many great things with feature flags.

Typically, the primary purpose of feature flags is to decouple feature releases from code deployments so developers can both deploy a new feature that is turned off (or hidden from the user), and they can turn on a new feature as needed (ie.

whentesting is complete, when the marketing team is ready, or whenever you, the developer, feel confident in the feature).

Some Cool Things Possible with Feature Flags

Canary Releases and Phased Roll-outs

After deploying a new feature, you can turn it on for just a handful of your users to gather feedback without risking your reputation on a broader audience. Sometimes, it’s a good idea to release a new feature in smaller phases to see how the infrastructure handles the load.

A/B Testing

Sometimes it’s hard to decide if we want to go forward with feature A or feature B, so we need to test first. Testing on live users provides high-quality data. Showing feature A to one group of users, while showing feature B to another group enables developers to measure which feature the user prefers.

Dogfooding

Tech companies, Okta included, usually use their products internally. So, whenever a new feature is on the horizon, it’s a good idea to test it on your organization first, to be sure that you are satisfied with the user experience and quality.

Emergency Kill Switch

“Never release on Friday!” — often said by experienced developers. Major issues tend to rise over the weekend when it is difficult to get hold of the development team and rollback. This toggle comes in handy when you need to immediately turn the latest feature off.

The Anatomy of a Feature Flag

At the end of the day, a feature flag in your code is a Boolean variable. A common practice is to place the new feature in one of the statements of a conditional.

if(isMyNewFeatureEnabled)
{ doTheNewThing();
}
else
{ doTheOldThing();
}

Where Does the Feature Flag Value Come From?

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

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