Флаги функций произвели революцию в мобильной разработке, позволяя переключать функции программного обеспечения в любой момент.
Современная мобильная разработка движется быстро; здесь, в нашей команде, мы выпускаем новую версию нашего приложения для iOS каждые две недели.
Если вам посчастливилось пережить момент расширения команды, вы также увидите точный момент, когда все начинает замедляться, потому что у вас есть несколько незавершенных вещей, которые не совсем готовы к производству.
Это типичная проблема масштабирования, когда вам нужно переосмыслить то, как вы и ваша команда выполняете повседневную работу (если вы сталкиваетесь с этим переходом, вам действительно следует взглянуть на Мобильные приложения в масштабе Гергели Ороша).
Для создания отличных функций требуется время. Как вы позволяете команде работать эффективно — свободно экспериментировать — и в то же время поддерживать готовое к выпуску приложение?
Флаги функций — это одна из стратегий, которую вы можете использовать, чтобы лучше справляться с увеличением трафика в репозиториях вашей команды.
Что такое флаг функции?
Некоторые люди называют их «переключателями функций», другие — «переключателями функций» или «элементами управления функциями». Все они являются названиями решения для управления функциями, которое позволяет разработчикам или менеджерам по продуктам изменять функциональные возможности продукта без развертывания нового кода.
Вы можете рассматривать флаг функции как точку остановки в своем коде, которая может изменить поведение вашего продукта. Вы можете разрешить разработчикам/тестерам/команде продукта изменять эти значения во время выполнения.
(…) пометка функций для экспериментов — это то же самое, что машинное обучение для ИИ, у вас не может быть второго без первого
JustEat Tech Blog
Общие сценарии
Флаги функций полезны для многих сценариев, типичных для мобильных команд разработчиков.
Избегайте долгоживущих ветвей
В этой среде вы можете позволить разработчикам объединять свой код, не работая над долгоживущими ветвями (каждый разработчик ненавидит решать скучные конфликты слияния каждый раз в течение нескольких дней или, что еще хуже, целых недель).
Флаги функций позволяют создавать MR небольшими и сфокусированными, основная ветвь может постоянно обновляться, и вы можете без проблем выпускать ночные сборки (*).
Уменьшить риск
Ошибки случаются — даже с лучшими разработчиками и строгими методами тестирования.
Представьте себе: после нескольких месяцев работы ваша команда только что выпустила новую потрясающую функцию, и вы очень хотите, чтобы ваши пользователи попробовали ее. К сожалению, все идет не так, как вы думали, и вы обнаружили неожиданную ошибку. Теперь вы можете либо развернуть оперативное исправление, либо отменить эту функцию.
Однако развертывание мобильных приложений не так просто, как для веб-приложений или сайтов, и с проверкой приложения вы можете подождать 1–2 дня. Это ужасно, так как вы рискуете потерять пользователей.
Вы должны сделать что-то немедленно!
С помощью флагов с дистанционным управлением вы можете временно отключить эту функцию, не торопясь с выпуском исправления.
A/B-тестирование
Флаги функций можно использовать для активации различных функций для групп пользователей. Например, вы можете разделить свою пользовательскую базу на несколько разделов и измерить эффективность конверсии, чтобы выбрать более высокие конверсии продукта, вовлеченность, рост, уровень подписки или доход.
Кампании продукта/ранний доступ
Команды по продуктам и маркетингу могут безопасно развертывать функции, когда они будут готовы, удаленно. Они могут предоставить доступ к функции группе элитных пользователей или новым пользователям для привлечения внимания.
Наш сценарий развития
В нашей команде мы используем пометку функций, чтобы закрыть незавершенные функции, чтобы мы могли продолжать создавать и тестировать их, не раскрывая их конечным пользователям до того, как они будут готовы. Мерж-реквесты короткие и целенаправленные, они входят в основную ветку, даже если они еще не завершены. Очевидно, что по умолчанию они отключены, пока все не будет готово, но команда может продолжить стандартный цикл релизов.
Чтобы упростить задачу, команда iOS разработала собственную библиотеку с открытым исходным кодом под названием RealFlags, которая позволяет без проблем обрабатывать флаги функций. Идея библиотеки состоит в том, чтобы использовать лучшие из новых языковых функций Swift для создания мощного уровня абстракции над несколькими поставщиками, которые вы можете использовать для настройки своей экспериментальной лаборатории. RealFlags создан с учетом Swift и подавляющего большинства основных концепций, которые мы использовали для его создания, представлены в текущем основном выпуске 5.x.
В посте ниже я опишу архитектуру и основной движок с примерами кода и предложениями, основанными на нашем опыте. Я хотел бы поблагодарить несколько библиотек с открытым исходным кодом, вдохновивших меня на следующую работу: JustTweak от JustEat, Override от Yahoo и FeatureFlags от Росса Батлера.
Что вы хотите от флагов функций
Как я уже сказал в начале поста, мобильная разработка движется быстро; продукт все еще развивается, ограничения меняются, ваш код легко превращается в устаревший код, который необходимо реорганизовать.
Наше новое приложение Swift родилось менее 4 лет назад, но за это время мы переписали несколько частей: функциональные флаги помогли нам сделать этот процесс постепенным. Фактически, мы перешли от старых реализаций к новым прозрачным для конечного пользователя способом.
Но продукт — это не только техническая задача (некоторые разработчики склонны забывать об этом). В качестве непрерывного эксперимента ваша продуктовая команда будет внедрять новые функции, включать/отключать существующие с помощью A/B-тестирования и так далее.
Таким образом, механизм флагов функций должен соответствоватьэтим требованиям:
- Это просто. Цель состоит в том, чтобы максимально скрыть сложность. Ваша команда должна иметь возможность легко определять и управлять флагами функций. На самом деле вы должны косвенно поощрять разработчиков к их использованию.
- Это шлюз — он должен действовать как шлюз для незавершенных функций. Разработчики/контроль качества/продукт должны иметь возможность протестировать процесс разработки, в то время как цикл выпуска может продолжаться без остановок.
- Поощряет хорошие привычки. Флаги должны быть информативными и простыми в упорядочении: в итоге вы можете установить множество различных флажков функций. Вероятность запутаться высока, и вы точно не захотите тратить время на поиски, где находится флаг и что он означает. Сама библиотека должна призывать к хорошим привычкам.
- Это абстрактно. Вы не хотите полагаться на конкретный сервис только для того, чтобы тратить время и ресурсы на переписывание всего с нуля, когда ваша команда разработчиков решит перенести все на что-то другое. Сторонние SDK не находится под контролем вашей команды, и поддержка нескольких поставщиков флагов функций отличается.
- Он поддерживает различные типы данных. Обычно флаг функции представляет собой просто логическое значение, но вам нужно использовать Int, Float, String или ваши собственные структуры, перечисления или классы! Вы должны быть в состоянии сохранить и установить их легко.
- Флажки функций можно обнаружить. Чтобы упростить работу разработчиков и позволить Qa/product играть с флагами, вы должны подготовить отличный пользовательский интерфейс для просмотра и изменения значений.
RealFlags выступает в качестве оркестровой библиотеки, когда вам нужно выбрать, как упорядочить свои флаги, и как запрашивать значения.
Введение
RealFlags предоставляет простой пользовательский интерфейс для взаимодействия и изменения ваших флагов. Флаги функций организованы в коллекции; вы можете определить коллекции, используя свои собственные критерии. После этого вы можете добавить какой-то скрытый раздел меню вашего приложения и указать браузеру показать их.
Ниже вы можете увидеть меню RealFlags, которое идеально вписывается в наше меню инструментов разработчика. Этот раздел по-прежнему отображается только тогда, когда приложение работает в режиме отладки или из установки TestFlight.
У нас есть разные флаги, и мы решили организовать их по областям компетенции, а последняя коллекция посвящена флагам с дистанционным управлением.
На момент написания этой статьи последней версией RealFlag была 1.2.0; вы можете использовать его через CocoaPods или SPM (подробнее).
Настраивать
Пришло время настроить ваш стек; Теперь вы готовы создать свою первую коллекцию. Предположим, вы хотите создать раздел, посвященный экспериментальным функциям пользователя.
Мы назовем его UserExperiments
: в этом разделе команда разработчиков может включить или отключить некоторые удивительные функции. Некоторыми флагами также можно управлять удаленно с помощью Firebase Remote Config.
По хорошей привычке RealFlags позволяет вам объявить план коллекции (коллекция — это структура, соответствующая FlagCollectionProtocol
), включая внутренние флаги. Хотя вы можете создавать коллекции коллекций, я настоятельно рекомендую избегать этого для ясности.
Флаг функции — это просто переменная, обернутая декоратором @Flag
. Технически говоря, это Property Wrapper, который определяет все атрибуты и метаданные флага. Короче говоря, оболочка свойства — это универсальная структура данных, которая инкапсулирует доступ для чтения/записи к свойству, добавляя некоторые дополнительные действия для расширения его семантики, избегая при этом дублирования кода.
Предположим, вы хотите создать флаг enableSSON
, который позволяет пользователю использовать единый вход для входа в приложение.
По умолчанию этот флаг выключен:
allowsSSOn
— это флаг Bool
, который по умолчанию равен false
(RealFlags поддерживает Int, String, Data, Date, URL, Dictionary, JSON и все типы данных соответствуют Codable
), который .
Поле description
используется для описания флага внутри браузера, поэтому будьте внимательны при его заполнении. Мы также указали key
: это строка, которую RealFlags будет использовать для запроса каждого поставщика данных (вы можете настроить его как хотите). Вы также можете установить дополнительные параметры конфигурации.
Поставщик данных — это просто источник данных; когда вы запрашиваете значение флага, запрос RealFlags — по порядку — каждый провайдер получает значение для этого ключа. Первый, кто ответит ненулевым значением, остановит цепочку с этим значением. Если ни один провайдер данных не может ответить (или вы не определили ни одного провайдера), поэтому возвращается значение default
.
Загрузить коллекцию
Поскольку приведенный выше код — это всего лишь схема нашей коллекции, теперь нам нужно ее загрузить. FlagsLoader
— это общий класс, отвечающий за чтение (путем отзеркаливания) структуры коллекции.
Обычно вы будете использовать один загрузчик для обработки одной коллекции. Когда вы создаете загрузчик, вы также указываете — по порядку — поставщиков данных, которые вы хотите использовать для запроса значения каждого флага (примечание: вы все равно можете исключить некоторых поставщиков данных для одного флага или запросить определенный тип поставщика данных, когда хотите чтобы получить значение флага).
На самом деле, вы можете выбрать два разных поставщика данных (но вы можете легко создать свой собственный по мере необходимости):
LocalProvider
: это провайдер с возможностью записи. Это позволяет вам загружать и устанавливать значения локально внутри песочницы, просто предоставляя URL-адрес места назначения файла.FirebaseRemoteProvider
: это провайдер только для чтения; позволяет вам получать значения непосредственно из предварительно настроенного сеанса Firebase (имейте в виду: ваша Firebase должна быть настроена черезFirebaseApp.configure()
перед вызовом этого провайдера).
Наш FFService
— это простой вспомогательный класс внутри нашего приложения, который содержит значение любого загрузчика:
Порядок поставщика данных в инициализации определяет приоритет запроса. При получении флага движок проверит цепочку провайдеров по порядку и выберет флаг из первой имеющейся конфигурации.
Запрос значений
Теперь вы можете запрашивать пользовательские экспериментальные функции, вызывая userExpsLoader: благодаря DynamicMemberLookup. Xcode автоматически заполнит для вас каждый флаг, определенный коллекцией. Просто волшебство!
Иногда вам может понадобиться получить значение флага внутри определенного провайдера. В этом случае вы можете обратиться к прогнозируемому значению . Мы получим значение LocalProvider
, игнорируя Firebase, который имеет более высокий приоритет в нашем загрузчике:
Как мы уже говорили, у нас могут быть вложенные коллекции:
Но какой ключ запрошен для boomFeature
? Если вы не укажете key
для флага, версия имени переменной в змее_case будет объединена с путем вниз к самой переменной. В этом случае запрошенный key
равен secrets/boom_feature
.
Установить значения
LocalProvider
— это пример доступного для записи поставщика данных: это означает, что вы можете изменить значение флага, и оно сохраняется между запусками приложения.
Поскольку мы поддерживаем несколько целевых приложений для разных стран, мы использовали setValue()
для хранения единого файла определений чертежа при изменении значений по умолчанию внутри условной инициализации:
Вычисляемый флаг
Иногда вам может понадобиться создать флаг функции, значение которого представляет собой комбинацию других флагов или значений среды выполнения, которые необходимо оценивать динамически.
Это идеальный пример использования computedValue
, которая является просто функцией обратного вызова, вызываемой перед запросом любого поставщика данных. Возврат ненулевого значения здесь останавливает цепочку.
Мы предлагаем ограничить использование этого шаблона, чтобы код оставался читабельным. Мы представили его для обработки нескольких крайних случаев, которые есть в нашем приложении.
Показать браузер
Мы начали рассматривать браузер флагов функций. Но мы не сказали, как это показать. Это просто:
Заключение
В этой статье я показал, как RealFlags может помочь в добавлении абстрактного, но гибкого подхода к пометке функций.
В настоящее время мы используем его в нашем флагманском продукте; если вы хотите внести свой вклад, предоставить дополнительные функции или поставщиков, посетите страницу проекта Github!