Привет 👋
🤔 Вы когда-нибудь оказывались в ситуации, когда вам хотелось бы запустить функцию для горстки пользователей, а затем развернуть ее для 100% пользователей на основе отзывов/аналитики? Или ваша команда только что закончила разработку огромной функции, но команда маркетинга/продукта говорит, что мы пока не будем ее запускать?
😖 В итоге вы создаете отдельную ветку фич и пытаетесь синхронизировать ее с основной веткой. Но на этом все не заканчивается. Через несколько недель вы захотите запустить эту функцию. Теперь вам придется снова запускать развертывание. Ситуация намного хуже с мобильными приложениями, где полное развертывание занимает 2-4 дня.
😭 О! Подождите? Вы нашли проблему. Вы хотите заблокировать пользователей от использования этой функции. Удачи!
👌 Чтобы спасти нас, разработчиков, от подобных ситуаций, у нас есть Feature Flags! Они помогают не только разработчикам, но и командам маркетинга, продуктам и продажам.
Что такое флаги особенностей?
Мне нравится определение, данное LaunchDarkly
Флаг функции – это процесс/шаблон разработки программного обеспечения, используемый для удаленного включения или отключения функциональности без развертывания кода. Новые функции можно развернуть, не делая их видимыми для пользователей. Флаги функций помогают отделить развертывание от выпуска, позволяя вам управлять полным жизненным циклом функции.
Флаги функций можно использовать для:
- Проведения A/B-тестов.
- Управление бета-программами.
- Уменьшения количества многократных развертываний или откатов.
- Предоставление доступа на основе ролей.
- Минимизация неудачных релизов за счет того, что функции сначала распространяются на меньшие группы.
Как только вы начнете использовать Feature Flags, пути назад уже не будет.
Добавление флагов функций в React
В данной реализации используется React ContextAPI. Прежде чем продолжить, убедитесь, что вы понимаете основы.
Давайте начнем с примера:
Представьте, что вы работаете над 💰 платежным шлюзом огромного сайта/приложения. Недавно он добавил два новых способа оплаты: Apple Pay и Google Pay.
Вы, будучи 10-кратным разработчиком, довольно быстро завершили обе интеграции, но команда маркетинга хочет задержать запуск Google Pay на несколько недель. Apple Pay запускается завтра.
Вы не хотите поддерживать отдельную ветку и повторно развертывать ее несколько недель спустя. Поэтому вы решили добавить флаги функций. 😎
Во-первых, давайте начнем с создания контекста для флагов возможностей.
// /contexts/FeatureFlags.js
export const FeatureFlags = React.createContext({});
Теперь давайте создадим Provider, который будет обертывать наше дерево React DOM.
// /contexts/FeatureFlags.js
export const FeatureFlags = React.createContext({});
export const FeatureFlagsProvider = ({ children }) => {
const [features, setFeatures] = React.useState({});
return (
<FeatureFlags.Provider value={{ features }}>
{children}
</FeatureFlags.Provider>
);
};
Наш контекст уже готов, осталось сделать еще несколько вещей. Сейчас мы можем обернуть дерево с помощью этого провайдера.
// index.js
// ... imports here
import App from "./App";
import { FeatureFlagsProvider } from "./contexts/FeatureFlags";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<FeatureFlagsProvider>
<App />
</FeatureFlagsProvider>
</StrictMode>
);
Теперь все, что нам нужно сделать, это получить наши функции. Я создал фиктивный API с помощью fastify. Вы можете проигнорировать эту часть.
// enabling cors for codesandbox
fastify.register(require("fastify-cors"), {
origin: /.csb.app$/
});
// feature flags route
fastify.get("/feature-flags", function(request, reply) {
const features = {
isGooglePayEnabled: true,
isApplePayEnabled: false
}
reply.send({ features });
});
Вернувшись к нашему контекстному файлу, давайте получим функции.
// /contexts/FeatureFlags.js
import { fetchFeatures } from 'api'
export const FeatureFlags = React.createContext({});
export const FeatureFlagsProvider = ({ children }) => {
const [isLoading, setIsLoading] = React.useState(true);
const [features, setFeatures] = React.useState({});
React.useEffect(() => {
(async () => {
try {
const data = await fetchFeatures();
if (data.features) {
setFeatures(data.features);
}
} catch (err) {
console.log(err);
} finally {
setIsLoading(false);
}
})();
}, []);
return (
<FeatureFlags.Provider value={{ features }}>
{isLoading ? "Loading..." : children}
</FeatureFlags.Provider>
);
};
Мы только что добавили useEffect
и состояние загрузки для нашего приложения.
И мы закончили! 🎉
Последний шаг – использовать это в наших компонентах.
// components/PaymentOptions.js
import { FeatureFlags } from "contexts/FeatureFlags";
const PaymentOptions = () => {
const { features } = React.useContext(FeatureFlags);
const handleClick = () => alert("Payment successful!");
return (
<>
<button className="btn" onClick={handleClick}>
Credit Card
</button>
{features.isApplePayEnabled && (
<button className="btn" onClick={handleClick}>
Apple Pay
</button>
)}
{features.isGooglePayEnabled && (
<button className="btn" onClick={handleClick}>
Google Pay
</button>
)}
</>
);
};
export default PaymentOptions;
🚀 Теперь мы можем запустить это приложение с полным контролем над недавно созданными функциями.
👏 Мы можем включить Google Pay, когда захотим, и пользователи сразу же увидят это. Если что-то пойдет не так, мы можем отключить оба режима оплаты.
reply.send({ isGooglePayEnabled: false, isApplePayEnabled: false});
И последнее, прежде чем вы уйдете, эта реализация является минимальной. Вы можете расширить его в соответствии с потребностями вашей команды. Несколько улучшений, которые мне приходят в голову:
- Добавление компонента
FeatureFlag
, который принимает параметрfeature
и скрывает или отображает дочерние элементы на основе этого параметра.
<FeatureFlag feature="isGooglePayEnabled">
<button onClick={handlePayment}>Google Pay</button>
</FeatureFlag>
- Добавление механизма кэширования и отката. Что если ваш вызов API завершится неудачей? В таком случае мы можем вернуться к нашей кэшированной версии. Это действительно интересно. 😉.
🔗 О, и вот ссылка на codesandbox, если вы хотите поиграть.
Вот и все, друзья! 👋
Надеюсь, эта статья вам чем-то помогла. Подумайте о том, чтобы поделиться ею с другими.
🤙 Если вы хотите поболтать о чем-нибудь, напишите мне в Twitter или LinkedIn.