Feature Flag and its implementation with Launchdarkly

28 / Jan / 2024 by Deepali Agarwal 0 comments

What are Feature Flags?

Feature flags (also known as Feature toggles, Release toggles, Feature Bits, Feature flippers, or Feature switches) are a powerful software development technique that allows you to enable or disable a functionality during runtime without changing code or requiring a redeploy.

At a code level, feature flags take the form of simple if-else conditions that determine at runtime which portions of code will be executed. If the flag is “on,” new code is executed, if the flag is “off,” the code is skipped. This lets you deploy new features without making them visible to users, or you can make features visible only to specific subsets of users and environments.

Toggles introduce complexity. We can keep that complexity in check by using smart toggle implementation practices and appropriate tools to manage our toggle configuration.

Feature Flag Benefits

  • Use Feature Flag to constantly merge the code before exposing it to the world: Feature flags encourage trunk-based development, which helps prevent merge conflicts from divergent code paths. Merge smaller changes more frequently into the main branch, keep feature flags off, and work from the main branch rather than work on long-lived feature branches.
  • Use Feature Flags for Testing in Production: It is often impossible to completely simulate the production environment in staging, feature toggles allow you to validate the functionality of new feature releases in production while minimizing the risk and quickly roll back the feature if necessary, via a kill switch.
  • Use Feature Flags for Small Test Releases: Expose features to a small audience/testers first, monitor the effects, and quickly roll back if necessary rather than having to go through another deployment cycle.
  • Release a feature for users that live in a specific country or fit a specific user profile.
  • Use Feature Flags for releasing features without deployment: Using feature flags, a team can modify a system’s behavior without making disruptive code changes to the live code. Thus, one major benefit of feature flags is making the feature live at a specific time. Features can be deployed and tested in production for testers, and once its time, it can be released for a larger audience without the need for deployment.
  • Rollback: If a bug is discovered in a new feature, it can be rolled back instantly by toggling it on or off without touching the source code.

Standard Practices

  • Feature flags should be named in a way that tells us very clearly what the feature flag is doing Ex. showHeader.
  • use camelCase while creating the keys in LaunchDarkly. This is to maintain consistency in React and node as React always access keys in camelCase form even if it’s added with hyphen and node will access the way it’s added in launchdarkly.
  • Enable “SDKs using Client-side ID” while creating the flag. Feature flags will only be accessible in React if this is enabled.
  • Avoid Dependencies Between Flags. The code needs to be so modular that different features can be turned on in any combination, so Don’t Nest Feature Flags.
  • Clean Up Obsolete Flags.
  • It’s best to keep feature flags short-lived and small in number.
  • Create them only when it makes sense. Don’t create it if the features are too small or for bug fixes. Don’t create multiple feature flags for the same feature.
  • Test Feature Flags: The feature flag needs to be tested with both the values (true/false) before deployment. When the feature flag is removed, testing needs to be conducted to check for any residues.
  • Use middleware in Node and custom hook call in React to use the feature flag. Flag name and context can be passed in the same and feature flag value can be retrieved. No need to write the entire code each time. Same functionality can be used even when there are no or multiple contexts.
  • Instead of polluting the codebase with lots of if conditions, use a single condition at the parent level component in React. So the component will be rendered if the condition is true, else it won’t be rendered. All API calls will happen within the child component. Also use Middleware to apply the feature flag in Node/SAP. Using it like this, makes it easy to remove the feature flag as code needs to be modified only at a single place.

React + LaunchDarkly

SDK version compatibility

  • The LaunchDarkly React SDK versions 3.0 and higher are compatible with React version 16.3.3 and higher.
  • The LaunchDarkly React SDK versions 2.x are compatible with React version 16.3.0 and higher.

Hooks

The LaunchDarkly React SDK offers two custom hooks. If you want to use these, then you must use React version 16.8.0 or higher.

  1. useFlags is a custom hook which returns all feature flags.
  2. useLDClient is a custom hook which returns the underlying LaunchDarkly JavaScript SDK client object.

Use withLDProvider higher-order component at the root of the application to initialize the React SDK and populate the context with the client and flags.

Flag Keys

LaunchDarkly primarily identifies feature flags by a key which must contain only alphanumeric characters, dots (.), underscores (_), and dashes (-). These keys are used in the SDKs to identify flags.

Always use camelCase while creating the keys in LaunchDarkly.

Setup:

  • Install Launch darkly Client SDK. Installed version ^3.0.6:
    npm install launchdarkly-react-client-sdk
  • Added Client side id in .env. Replace CLIENT_SIDE_ID with Client-side ID provided in the LaunchDarkly -> Account Settings -> Projects -> Select your Project -> Environments.
    REACT_APP_FEATURE_FLAG_CLIENT_ID=CLIENT_SIDE_ID

  • Initialize using withLDProvider in App.js:
    export default withLDProvider({
      clientSideID: Config.featureFlagClientID,
      options: {
        bootstrap: "localStorage",
      },
    })(App);
  • Added a custom hook that fetches feature flags based on contexts. Need to pass flagKey, userId and context in the hook.
    const FeatureFlag = (
      flagKey,
      userId,
      contextKind = "anonymous",
      contextDetails = {}
    ) => {
      const featureFlags = useFlags();
      const ldClient = useLDClient();
      useEffect(() => {
        let context = {};
        if (contextKind === "anonymous") {
          context = {
    	kind: "user",
    	anonymous: true,
          };
        } else {
          context = {
    	kind: contextKind,
    	key: "key-" + userId,
    	...contextDetails,
          };
        }
        ldClient?.identify(context);
      }, [ldClient, contextDetails]);
    		  
      return { featureFlagValue: featureFlags[flagKey] };
    };
    

    To create an anonymous context, specify the anonymous property and omit the key property. The client will automatically set the key to a LaunchDarkly-specific, device-unique string that is consistent between app restarts and device reboots.

Using a feature flag in any file

Ex. Show a header on all pages behind the feature flag, when the feature flag is turned on. When the flag is turned off, the header is gone. A feature flag named showHeader is created in Launch Darkly.

A custom hook is called by passing the flag key and context details. The header is only rendered if featureFlagValue is true.

Also, a new component will be created for the feature and that component will only be rendered if featureFlagValue is true. All the API calls will be added within that component. This is necessary for security reasons and to avoid calling unnecessary APIs.

With context:

	const { featureFlagValue } = getFeatureFlag("showHeader", 5, "user", {
	  email: userEmail,
	});

Without context:

	const { featureFlagValue } = getFeatureFlag("showHeader");
        {featureFlagValue && ( 
            <FeatureFlagHeader />
        )}

Alternatively, if you have to call the api within that specific component, write it as below. dispatch(getFFHeaderData()) fetches data and stores it in redux, then the same can be fetched in props featureFlagHeaderData using selectors.

useEffect(() => {
	  featureFlagValue && dispatch(getFFHeaderData());
	}, [featureFlagValue]);
	  
	{featureFlagValue && featureFlagHeaderData && (
          <div className="ffHeader">{featureFlagHeaderData}</div>
        )}

To remove feature flags, remove all instances of a feature flag in any specific file. It will be easy in the case of a new component as we only need to remove 1 condition and custom hook call and it’s good to go.

Context

Version 3 of the React Web SDK replaces users with contexts. Contexts let you create targeting rules for feature flags based on a variety of different information, including attributes pertaining to users, organizations, devices, locations and more. Multi-contexts are combinations of several context kinds into one context. Any unique combination of one or more contexts that have encountered a feature flag is a context instance.

	"context": {
	  "kind": "multi",
	  "user": {
	    "key": "user-key-123abc",
	    "name": "Anna",
	    "email": "anna@globalhealthexample.com",
	    "jobFunction": "doctor"
	  },
	  "organization": {
	    "key": "org-key-123abc",
	    "name": "Global Health Services",
	    "address": {
		"street": "123 Main Street",
		"city": "Springfield"
	    }
	  }
	}	   

Node + LaunchDarkly

SDK version compatibility

The 7.0 version of the SDK is compatible with Node.js versions 13 and higher.

Setup:

  • Install Launch darkly Server SDK. Installed version ^8.1.0:
    npm install launchdarkly-node-server-sdk
  • Added SDK Key in .env. Replace SDK_KEY with SDK Key provided in the LaunchDarkly -> Account Settings -> Projects -> Select your Project -> Environments.
    FEATURE_FLAG_SDK_KEY=SDK_KEY

  • Created Server.js that initializes LD if not already initialized.
    const client = LaunchDarkly.init(appSettings.featureFlagSDKKey);
  • A function getFeatureFlag returns feature flag value when passed flag key and context details.
    export default async function getFeatureFlag(
      flagKey,
      userId,
      contextKind = "anonymous",
      contextDetails = {}
    ) {
      if (!featureFlagClient) {
        featureFlagClient = await initialize();
      }
    
      let context = {};
      if (contextKind === "anonymous") {
        context = {
          kind: "user",
          key: "key-" + userId,
          anonymous: true,
        };
      } else {
        context = {
          kind: contextKind,
          key: "key-" + userId,
          ...contextDetails,
        };
      }
    
      return await featureFlagClient.variation(flagKey, context);
    }
    
  • Created a middleware (isFeatureAllowed) that calls getFeatureFlagand checks if the feature Flag is true / false. If it’s true, the API will be accessible, else it will return access denied messages.
    async function isFeatureAllowed(req, res, next, flagName, contextKind, contextDetails) {
      try {
        const featureFlag = await getFeatureFlag(flagName, contextKind, contextDetails);
        if (featureFlag) {
          next();
        } else {
          throw {};
        }
      } catch (error) {
        return res.json({
          success: false,
          code: 400,
          message: "This feature is disabled or you do not have access to it.",
        });
      }
    }
    			  
    
  • Call Middleware from all the controllers that require the feature flag with flag details and context. To Remove any feature flag, we just need to remove this middleware from the controller.
    (req, res, next) =>
      middlewares.isFeatureAllowed(req, res, next, "show-header", "user", {
        email: req.user.email,
    }),
    

The above were some insights about Feature Flag and its implementation with Launchdarkly. Check out our other blog posts for more insights.

FOUND THIS USEFUL? SHARE IT

Leave a Reply

Your email address will not be published. Required fields are marked *