Building Robust Feature Flags in JavaScript: From Basics to Gradual Rollouts

Feature flagging is a powerful technique for delivering features to users gradually, allowing you to test, iterate, and deploy new functionality without the risk of widespread failures. In this post, we'll take a deeper dive into how to implement a robust feature flagging system in JavaScript, covering best practices for managing flags, evaluating them securely, and ensuring smooth rollouts. Step 1: Understand the Basics of Feature Flags Feature flags (or feature toggles) are conditional checks in your application code that control whether a feature is enabled or disabled. Instead of deploying changes to users immediately, you can use flags to toggle features on or off in real-time, without requiring additional code deployments. For example, imagine you have a new "dark mode" feature: const flags = { darkMode: true, }; This flag can be checked in the UI code to decide whether to display the dark mode interface: if (flags.darkMode) { enableDarkMode(); } Step 2: Define Flag States Dynamically The flexibility of feature flags comes from being able to control their state dynamically. A flag can be set from a remote configuration server, allowing you to change feature availability at runtime. However, there are challenges in ensuring this works well with network failures or slow API responses. To handle this, we can use a default flag set while attempting to load remote flags asynchronously: const defaultFlags = { darkMode: false, }; async function loadFlags() { try { const response = await fetch('/api/feature-flags'); if (!response.ok) throw new Error('Failed to load flags'); return await response.json(); } catch (e) { console.error('Using default flags due to error:', e); return defaultFlags; } } This ensures your app continues to work, even when the remote config service is temporarily unavailable. Step 3: Store Flags in Local Storage for Persistence One of the key features of a resilient flag system is persistence. Storing flag values in localStorage or IndexedDB ensures that flags are preserved across page reloads, reducing the impact of network failures or initial loading delays. async function getFlags() { const storedFlags = localStorage.getItem('featureFlags'); if (storedFlags) { return JSON.parse(storedFlags); } const flags = await loadFlags(); localStorage.setItem('featureFlags', JSON.stringify(flags)); return flags; } This allows users to continue experiencing the correct features, even if they refresh the page, as the flags will be loaded from local storage. Step 4: Implementing Gradual Rollouts with Bucketing A powerful use case for feature flags is controlling the rollout of new features gradually. This allows you to test new functionality with a subset of users before a full-scale release. One way to achieve this is by implementing bucketing based on user identifiers, like their email address or user ID. function getBucket(userId) { return parseInt(userId.charCodeAt(0)) % 10; } function shouldEnableFeature(userId, flag) { const bucket = getBucket(userId); return flag === 'newFeature' && bucket < 5; // 50% of users get the feature } By adjusting the bucket size, you can control the percentage of users who see the new feature. Step 5: Evaluating Flags in React or Other SPAs In single-page applications (SPAs) like React, it's crucial to ensure that feature flags are evaluated globally, so they can be accessed across the app and trigger appropriate changes dynamically. const FlagsContext = React.createContext({}); export function FlagsProvider({ children }) { const [flags, setFlags] = useState({}); useEffect(() => { getFlags().then(setFlags); }, []); return ( {children} ); } This context-based approach ensures that your feature flags are available globally, without needing to pass them manually between components. ✅ Pros:

May 2, 2025 - 04:13
 0
Building Robust Feature Flags in JavaScript: From Basics to Gradual Rollouts

Feature flagging is a powerful technique for delivering features to users gradually, allowing you to test, iterate, and deploy new functionality without the risk of widespread failures. In this post, we'll take a deeper dive into how to implement a robust feature flagging system in JavaScript, covering best practices for managing flags, evaluating them securely, and ensuring smooth rollouts.

Step 1: Understand the Basics of Feature Flags

Feature flags (or feature toggles) are conditional checks in your application code that control whether a feature is enabled or disabled. Instead of deploying changes to users immediately, you can use flags to toggle features on or off in real-time, without requiring additional code deployments.

For example, imagine you have a new "dark mode" feature:


const flags = {
  darkMode: true,
};

This flag can be checked in the UI code to decide whether to display the dark mode interface:


if (flags.darkMode) {
  enableDarkMode();
}

Step 2: Define Flag States Dynamically

The flexibility of feature flags comes from being able to control their state dynamically. A flag can be set from a remote configuration server, allowing you to change feature availability at runtime. However, there are challenges in ensuring this works well with network failures or slow API responses.

To handle this, we can use a default flag set while attempting to load remote flags asynchronously:


const defaultFlags = {
  darkMode: false,
};

async function loadFlags() {
  try {
    const response = await fetch('/api/feature-flags');
    if (!response.ok) throw new Error('Failed to load flags');
    return await response.json();
  } catch (e) {
    console.error('Using default flags due to error:', e);
    return defaultFlags;
  }
}

This ensures your app continues to work, even when the remote config service is temporarily unavailable.

Step 3: Store Flags in Local Storage for Persistence

One of the key features of a resilient flag system is persistence. Storing flag values in localStorage or IndexedDB ensures that flags are preserved across page reloads, reducing the impact of network failures or initial loading delays.


async function getFlags() {
  const storedFlags = localStorage.getItem('featureFlags');
  if (storedFlags) {
    return JSON.parse(storedFlags);
  }

  const flags = await loadFlags();
  localStorage.setItem('featureFlags', JSON.stringify(flags));
  return flags;
}

This allows users to continue experiencing the correct features, even if they refresh the page, as the flags will be loaded from local storage.

Step 4: Implementing Gradual Rollouts with Bucketing

A powerful use case for feature flags is controlling the rollout of new features gradually. This allows you to test new functionality with a subset of users before a full-scale release. One way to achieve this is by implementing bucketing based on user identifiers, like their email address or user ID.


function getBucket(userId) {
  return parseInt(userId.charCodeAt(0)) % 10;
}

function shouldEnableFeature(userId, flag) {
  const bucket = getBucket(userId);
  return flag === 'newFeature' && bucket < 5;  // 50% of users get the feature
}

By adjusting the bucket size, you can control the percentage of users who see the new feature.

Step 5: Evaluating Flags in React or Other SPAs

In single-page applications (SPAs) like React, it's crucial to ensure that feature flags are evaluated globally, so they can be accessed across the app and trigger appropriate changes dynamically.


const FlagsContext = React.createContext({});

export function FlagsProvider({ children }) {
  const [flags, setFlags] = useState({});

  useEffect(() => {
    getFlags().then(setFlags);
  }, []);

  return (
    
      {children}
    
  );
}

This context-based approach ensures that your feature flags are available globally, without needing to pass them manually between components.

Pros: