Feature Flags in JavaScript with Vanna

Published August 29, 2018 by wamberg

I See a Red Button

Vanna is an open source feature flagging library written and used at PBS. Let's dive into the JavaScript client. To set up our tutorial, a story.

Mick is a front-end developer. The design team asked Mick to change the color of a red button to black. Product Management isn't ready to go all-in on the black button. Design and Product Management ask our prodigal engineer if there's a way to hedge our bets. They want to show the experimental black button to a small group of users. Mick smiles and puts on his shades. 😎

Here's a brief example of how Vanna lets you do this:

// 👇 An instance of vanna client - implementation to come
import features from "app/features"; 

const paintItBlack = features.variation("paint-it-black")

if (paintItBlack) {
  // Render experimental black button
} else {
  // Render red button
}

This is feature flagging at its simplest. With feature flagging you can merge to the trunk more frequently. You can reduce risk by limiting new, volatile code to a subset of users. Vanna let's you do this in a way that's controlled outside of your application code. This unlocks another trait of continuous delivery.

A desirable goal of continuous delivery is to decouple deployments from releases. Deployments are the act of moving code to a server. Releases are the act of making code paths available to users. You can read more in this Hacker Noon article. To decouple releases from deployments Vanna receives its features from a JSON response. This allows us to update feature availability without doing a code deployment.

Creating Features

Let's dive into the shape of the feature response. The response looks like this:

{
  "features": {
    "paint-it-black": {
      "slug": "i-want-to-paint-it-black",
      "enabled": true,
      "targetSegment": ["alpha-tester"]
    }
  }
}

The feature response contains any number of feature objects. In our sample there is one feature, "paint-it-black". The feature has three properties:

  • "slug" - This names the feature. It's useful for feature identification when you're only given the feature values. We'll use it for overriding feature availability in our advanced example.
  • "enabled" - This key makes the feature available. Think of it as the master circuit breaker. If this is false, the feature will be off for everyone.
  • "targetSegment" - Features target users. You make a feature available to groups of users with this key. We'll see how users identify as a userSegment when we instantiate a new VannaClient.

There is no console interface to create this JSON response at the moment. Right now we'll write the JSON by hand and make it accessible through a CDN. An admin interface and API service to create this response is a future enhancement. Hand-crafting the JSON was the smallest step we could take towards developing the Vanna library. Taking this MVP approach makes it easier for us to experiment and iterate.

Using vanna-js

In our simple example we assumed the availability of the client library. Let's implement it.

We'll set userSegment based on the presence of a cookie. See our previous post on setting cookies for feature flags.

// app/features.js
import { VannaClient } from "@pbs/vanna";
import Cookies from "js-cookie";

const isAlphaTester = Cookies.get("alpha-tester");

const client = new VannaClient({                                              
  uri: "https://cdn.com/features.json",                    
  userSegment:  isAlphaTester ? "alpha-tester" : "regular",           
  fallbacks: {                                                                
    "paint-it-black": false                                              
  }                                                                          
});                                                                           

When you instantiate a new VannaClient you're responsible for:

  • uri - This is the location of the JSON feature control response.
  • userSegment - This is the user's group. Vanna enables the feature for this user on a match to an enabled "targetSegment".
  • fallbacks - This sets the default behavior for feature flags. Note that a fallback must be set for every feature in the JSON response.

We can now use Vanna to finish our task. In our initial example we created a boolean to split our code path with:

const paintItBlack = features.variation("paint-it-black")

Vanna's variation() method takes the feature's "targetSegment" and client's userSegment into consideration. On a match between the two the method returns true.

With this tutorial you can use Vanna as a feature flagging library. You can decouple deployments from releases. You can ship software more quickly with a lower risk. Using Vanna in this way for feature flagging is perfect for simple use cases. Advanced options are available for power users who need more customization.

Overriding Variations

Controlling features with a single userSegment seems coarse. What if we want finer control? What if I want to enable a specific feature regardless of my userSegment? The Vanna client allows you to override variation eligibility. We can extend our previous post about toggling flags on feature-specific cookies. We'll allow Vanna to opt-in to a feature based on the presence of named cookies. The following highlighted blocks show how we can add to our previous Vanna client:

// app/features.js
import _ from "lodash";
import { VannaClient, getFeatureVariation } from "@pbs/vanna";
import Cookies from "js-cookie";

function getVariationOverride(featureSlug) {
  const featureKey = `feature:${featureSlug}`;
  const overrideValue = Cookies.get(featureKey);
  if (overrideValue) {
    return overrideValue === "true";
  }
  return undefined;
}

const isAlphaTester = Cookies.get("alpha-tester");

const client = new VannaClient({                                              
  uri: "https://cdn.com/features.json",                    
  userSegment:  isAlphaTester ? "alpha-tester" : "regular",           
  fallbacks: {                                                                
    "paint-it-black": false                                              
  },
  _overrides: {
    getFeatureVariation: (feature, { userSegment }) => {
      const variation = getFeatureVariation(feature, { userSegment });
      const overrideVariation = getVariationOverride(feature.slug);
      return _.isUndefined(overrideVariation) ? variation : overrideVariation;
    }
  }
});                                                                           

With this additional code a user can opt-in to features that are not part of their userSegment. In our example if a user doesn't have the "alpha-tester" cookie, but does have a "feature:i-want-to-paint-it-black" cookie, they will see the black button. The opposite use case also works. An "alpha-tester" can opt-out of a feature by setting a named cookie to "false". This variation override allows for finer control over feature availability. We've used cookies to override the variation, but you could use local storage or anything that's available in JavaScript.

The vanna-js-client is an open source project. Please check out the brief, readable source code. It's a lightweight way to add feature flags to your JS project.

Twitter avatar
Contact me on Twitter!
@AccelDelivery
Want to share a link about software delivery?
Have feedback on what you read?
I'd love to hear from you.