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

Механизм переключения функций

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

FeatureToggleUtils.ts

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

// I have created my own typed interface to interact with local and session storage.
import { typedLocalStorage, typedSessionStorage } from '@utils/web-storage';

const featureFlagKey = 'featureFlags';
const persistFeatureKey = 'persistFeatureFlag';

// All the features that you want to be toggle on/off should be added here.
// To ensure that we type check the feature names across our application.
export type SupportedFeatureFlags = 'featureOne';

/*
 * Please add all the features that you want to be toggle on by default here.
 * Remember that all the features that are listed here will be available to the users in production,
 * since this is the default active features.
 */
const defaultActiveFeatures: SupportedFeatureFlags[] = [];

/**
 * @param featureFlag
 * @returns boolean
 * @description This function will check if the feature should be displayed 
 * or not. The feature can be toggled on by the url query, by local storage
 * or set as default active feature.
 * @example shouldDisplayFeature('myFeatureName') && <MyFeatureComponent />
 * @example The feature can be toggled and persisted by the url query,
 * (url)?featureFlags=[featureName]&persistFeatureFlag=true
 */
export const shouldDisplayFeature = (featureFlag: SupportedFeatureFlags): boolean => {
  // Check if feature should be persisted in session storage, (url)?persistFeatureFlag=true
  if (shouldPersistInSession()) {
    addFeatureFlagToSessionStorage(featureFlag);
  }

  return (
    isDefaultActivatedFeature(featureFlag) ||
    isFeatureActivatedByUrl(featureFlag) ||
    isFeatureActivatedBySessionStorage(featureFlag) ||
    isFeatureActivatedByLocalStorage(featureFlag)
  );
};

// Check if the feature is one of the default active features
const isDefaultActivatedFeature = (featureFlag: SupportedFeatureFlags): boolean => {
  return defaultActiveFeatures.includes(featureFlag);
};

// Check if feature includes in the url query, (url)?featureFlags=[featureName]
const isFeatureActivatedByUrl = (featureFlag: SupportedFeatureFlags): boolean => {
  const urlParams = new URLSearchParams(window.location.search);
  const featureParam = urlParams.get(featureFlagKey);

  if (featureParam) {
    const features = featureParam.split(',');
    return features.includes(featureFlag);
  }

  return false;
};

// Check if feature includes in local storage, featureFlags: ["featureName"]
const isFeatureActivatedByLocalStorage = (featureFlag: SupportedFeatureFlags): boolean => {
  const featureFlagsFromStorage = typedLocalStorage.getItem<string[]>(featureFlagKey) || [];
  return featureFlagsFromStorage.includes(featureFlag);
};

// Check if feature includes in session storage, featureFlags: ["featureName"]
const isFeatureActivatedBySessionStorage = (featureFlag: SupportedFeatureFlags): boolean => {
  const featureFlagsFromStorage = typedSessionStorage.getItem<string[]>(featureFlagKey) || [];
  return featureFlagsFromStorage.includes(featureFlag);
};

// Check if the feature should be persisted in session storage, (url)?persistFeatureFlag=true
const shouldPersistInSession = (): boolean => {
  const urlParams = new URLSearchParams(window.location.search);
  const shouldPersistInSession = urlParams.get(persistFeatureKey);
  return !!shouldPersistInSession;
};

// Add feature to session storage to persist the feature in the current session
const addFeatureFlagToSessionStorage = (featureFlag: SupportedFeatureFlags): void => {
  const featureFlagsFromStorage = typedSessionStorage.getItem<string[]>(featureFlagKey) || [];
  typedSessionStorage.setItem<string[]>(featureFlagKey, [...featureFlagsFromStorage, featureFlag]);
};

Понимание кода

Определения функций

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

Активные функции по умолчанию

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

Функция mustDisplayFeature

Основная функция, экспортированная из FeatureToggleUtils.ts, отвечает за определение того, следует ли отображать функцию, — shouldDisplayFeature. Эта функция учитывает ряд условий, чтобы решить, должна ли функция быть активной. Эти условия включают в себя:

  1. Активация по умолчанию. Если функция находится в массиве defaultActiveFeatures, она считается активной по умолчанию и доступна всем пользователям в рабочей среде без необходимости устанавливать флаг функции. Это позволяет разработчикам легко включать функцию, но в то же время ее можно легко отключить, если необходим откат.
  2. Параметры URL-запроса. Если функция включена в параметры URL-запроса, она активируется на основе параметра запроса. Следующий URL-адрес представляет собой пример того, как можно активировать myAwesomeFeature,yourdomain.com?featureFlag=myAwesomeFeature..
  3. Хранилище сеанса. Если persistFeatureFlag параметр запроса установлен, функция сохраняется в хранилище сеанса и сохраняется на всех страницах в одном сеансе. Это замечательно, если пользователям необходимо перемещаться между страницами, и мы хотим, чтобы эта функция активировалась при каждом просмотре страницы. URL-адрес может выглядеть так yourdomain.com?featureFlag=myAwesomeFeature&persistFeatureFlag=true . Это позволяет легко делиться новыми функциями с людьми, не имеющими технических знаний, чтобы они могли протестировать эту функцию и оставить отзыв о ней.
  4. Локальное хранилище.Для разработчиков может быть затруднительно всегда манипулировать URL-адресом для активации функции. Затем разработчики могут добавить флаг функции в локальное хранилище, чтобы гарантировать, что эта функция присутствует в каждом сеансе. Это можно сделать, добавив пару ключ-значение featureFlags: ["myAwesomeFeature"] в локальное хранилище.

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

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

  • isFeatureActivatedByDefault: Эта функция проверяет, активирована ли функция массивом функций по умолчанию, defaultActiveFeatures.
  • isFeatureActivatedByUrl: эта функция проверяет, активирована ли функция, на основе параметров запроса в URL-адресе.yourdomain.com?featureFlag=myAwesomeFeature
  • isFeatureActivatedByLocalStorage: Эта функция проверяет, активирована ли функция, на основе значений, хранящихся в локальном хранилище.
  • isFeatureActivatedBySessionStorage: Эта функция проверяет, активирована ли функция, на основе значений, хранящихся в хранилище сеанса.

Постоянство хранилища сеансов

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

Пример использования в React

Код ниже демонстрирует, как легко мы можем скрыть что-то за флагом функции. Чтобы иметь возможность отображать ‹MyAwesomeFeatureComponent/><, флаг функции myAwesomeFeature должен быть активирован в URL-адресе, сеансе/локальном хранилище или предоставлен в activeByDefaultArray.

import { shouldDisplayFeature } from '@utils/featureToggling';

const MyPage = () => {
  return (
   <>
    <h1>Hello</h1>
    {shouldDisplayFeature('myAwesomeFeature') && <MyAwesomeFeatureComponent/>}
   </> 
 )
}

Заключение

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

На простом английском языке

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