Рубрики
Без рубрики

Создайте свой собственный менеджер флагов функций в колбе / Python

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

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

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

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

if feature_flag('NEW_FEATURE', 'ENABLED'):
    # show new feature

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

if feature_flag('MY_EXPERIMENT', 'VARIANT_A'):
    # show variant a
elif feature_flag('MY_EXPERIMENT', 'VARIANT_B'):
    # show variant b
else:
    # show control

Купить против сборки

Люди гораздо умнее и более опытные, чем я широко написал по поводу принятия решения по сборке, например, Смотрите это отлично Анализ Из Камилле Фурнье, автор Путь менеджера Отказ Я не буду возобновить всю тему здесь, но достаточно сказать, чаще, чем инженеры, как правило, наклонны, чтобы выбрать его, правильное решение должно покупать. Это верно, по крайней мере, в контексте инженерных групп, работающих над производственными системами любого масштаба.

По состоянию на время этого письма Mid-2021 Launchdarkly является ведущим решением SaaS для функций. Исходя из размера своего бизнеса, многие разработчики, кажется, находят значение в хорошей системе для управления своими функциями. При 10 долларов США/пользователь, безусловно, намного дешевле пользоваться сферой полки, чем проводить свои инженерные часы (выставлено на гораздо более высокую скорость!) На строительство ваших собственных.

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

Создайте свои собственные флаги функций менеджер

Я всегда знал абстракцию для менеджера флагов функций как «привратник», работая на Facebook и с многочисленными бывшими Faceobile, так как это имя внутреннего обслуживания для их функций и экспериментов рамки . Это броское имя и имеет преимущество того, чтобы быть удобно сокращенным как ГК в коде. Я буду использовать это здесь также.

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

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

Итак, чтобы обобщить, вам понадобится:

  1. Менеджер флагов объектов (привратник)
  2. Флаг функции Configs (Configs Gatekeeker)
  3. Проводит вещи аккуратно, чтобы все было легко использовать

Ниже приведен код в Python, для веб-приложения, используя Flask Framework. Надеюсь, это достаточно просто, чтобы прочитать здесь, и простираться на другие языки и рамки!

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

gatekeeper.py

from gatekeeper_config import FF_CONFIG_MAP


class Gatekeeper(object):
    def __init__(self, user_id=None,
                 app=None,
                 request=None,
                 session=None,
                 config_map=None):

        self.user_id = user_id
        self.app = app
        self.request = request
        self.session = session

        self.config_map = config_map if config_map else FF_CONFIG_MAP

    def ff(self, *args, **kwargs):
        '''Shorthand wrapper for `feature_flag`.'''
        return self.feature_flag(*args, **kwargs)

    def feature_flag(self, flag, variant):
        return self.get_feature_flag_variant(flag) == variant

    def ff_variant(self, *args, **kwargs):
        '''Shorthand wrapper for `get_feature_flag_variant`.'''
        return self.get_feature_flag_variant(*args, **kwargs)

    def get_feature_flag_variant(self, flag, user_id_override):
        config = self.config_map.get(flag)
        if not config:
            return None

        variant = config.get_variant(
            user_id=user_id_override or self.user_id,
            app=self.app,
            request=self.request,
            session=self.session)
        return variant.name

    def get_config_map(self):
        return self.config_map

    def get_browser_override_variants(self, request):
        return json.loads(request.cookies.get('gatekeeper', '{}'))

    def set_browser_override_variant(self, request, flag, variant):
        config = self.config_map.get(flag)
        if not config:
            return None

        return config.set_browser_override_variant(request, variant)


def initialize_gatekeeper(user_id=None, app=None, config_map=None):
    from flask import current_app
    from flask import request
    from flask import session
    from flask_login import current_user

    if not app:
        app = current_app

    if user_id is None and current_user and not current_user.is_anonymous:
        user_id = current_user.id

    gk = Gatekeeper(
        user_id=user_id,
        app=app,
        request=request,
        session=session,
        config_map=config_map)
    return gk

gatekeeper_config.py

from abc import ABC
from abc import abstractmethod
from enum import Enum
import json

import flask


class FeatureFlagConfig(ABC):
    FLAG_NAME: str
    VARIANTS_ENUM_STR: str

    def __init__(self, overrides=None):
        self.variants_enum = Enum(
            f'{self.__class__.__name__}Variants', self.VARIANTS_ENUM_STR)
        self.overrides = overrides

    def get_variants(self):
        return list(self.variants_enum.__members__.keys())

    @abstractmethod
    def _get_variant(
            self,
            user_id: Optional[int] = None,
            app: Optional[Flask] = None,
            request: Optional[Request] = None,
            session: Optional[Any] = None):
        pass

    def get_variant(self, user_id: Optional[int] = None, app: Optional[Flask] = None,
                    request: Optional[Request] = None, session: Optional[Any] = None):
        override_variant = self.get_override_variant(
            user_id=user_id, app=app, request=request, session=session)
        if override_variant:
            return override_variant
        else:
            return self._get_variant(
                user_id=user_id, app=app, request=request, session=session)

    def get_override_variant(
            self, user_id=None, app=None, request=None, session=None):
        if request:
            browser_override_variant = self.get_browser_override_variant(request)
            if browser_override_variant:
                return browser_override_variant
        if self.overrides:
            for variant, user_ids in self.overrides.items():
                if user_id in user_ids:
                    return self.variants_enum[variant]
        return None

    def set_browser_override_variant(self, request, variant):
        browser_override_variants = self.get_browser_override_variants(request)
        if variant == '':
            browser_override_variants.pop(self.FLAG_NAME, None)
        else:
            browser_override_variants[self.FLAG_NAME] = variant

        response = flask.make_response()
        response.set_cookie(
            'gatekeeper',
            json.dumps(browser_override_variants))
        return response

    def get_browser_override_variants(self, request):
        return json.loads(request.cookies.get('gatekeeper', '{}'))

    def get_browser_override_variant(self, request):
        browser_override_variants = self.get_browser_override_variants(request)
        browser_override_variant = browser_override_variants.get(self.FLAG_NAME)
        if browser_override_variant:
            return self.variants_enum[browser_override_variant]
        else:
            return None


class MyNewFeatureFFConfig(FeatureFlagConfig):
    FLAG_NAME = 'MY_NEW_FEATURE'
    VARIANTS_ENUM_STR = 'VISIBLE NOT_VISIBLE'
    DESCRIPTION = 'Gate visibility of my new feature during development'

    def _get_variant(self, user_id=None, app=None, request=None, session=None):
        if app and app.config.get('IS_DEV'):
            return self.variants_enum.VISIBLE
        elif user_id and user_id in [1, 2, 3]:  # Team user ids
            return self.variants_enum.VISIBLE
        else:
            return self.variants_enum.NOT_VISIBLE


FF_CONFIG_MAP: FFConfigMap = {
    'MY_NEW_FEATURE': MyNewFeatureFFConfig()
}

В приложении

# All the usual app setup stuff here... 

app = create_app()

@app.before_request
def register_gatekeeper():
    # Put imports here to avoid circular import issues.
    from flask import request

    import gatekeeper

    request.gk = gatekeeper.initialize_gatekeeper(app=app)

Использование привратника

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

Например, в коде веб-обработчика Python это может выглядеть:

if request.gk.ff('MY_NEW_FEATURE', 'VISIBLE'): 
    # Show new feature

Или в шаблоне шаблона Jinja это может выглядеть:

{% if request.gk.ff('MY_NEW_FEATURE', 'VISIBLE') %}
    {# Show new feature #}
{% endif %} 

Последние мысли

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

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

Оригинал: “https://dev.to/triketora/build-your-own-feature-flags-manager-in-flask-python-1jo0”