6 min read
Feature Flags: Building better frontend distribution

Architecturing gradual rollouts and smooth A/B testing

One of the key “Distribution” related items when it comes to Frontend systems is Feature Flags. It shows “Maturity” in your systems. It shows “Fearlessness” while dealing with multiple branches.

I recently introduced feature flags in one of my systems. The goal : to rollout features gradually, smoothly and (most importantly) as per the rollout schedule.

  • No production surprises.
  • No deployment chasing between teams.
  • Ability to roll back as quick as it can get.
  • And the wonderful, A/B testing.

How we did it

In our system, we developed a very minified version of the feature flag (as our need is satisfied with that). The feature flag declaration happens on a different repository, which can be accessed only by seniors of the product team. The developments starts only when a feature is declared in that repo. By default, every feature is enabled for Dev and Local environments and disabled for Stage and Production.

Since the only job of this repo is to send an object, it is dead fast (which removes the need of caching layer), and we only call it at the highest level and store as a global state in frontend build (more on that in the later part). We also poll it at an interval and compare/update the global state, as we don’t want any user to stay longer on a screen which is now rolled back.

We manage the feature at three levels :

  1. Route level
  2. Page level
  3. Component level

Each level at details

Route level - a route will not be available in a specific environment, until it is allowed in feature flag. This way, we do not have to carry a lot of condition through out the codebase, as it is restricted at the entry point. The only other area needs a condition is the buttons and links triggering this route.

{isProductionEnable("MultiChat") && (
    <Route path="multi-chat" element={<MultiChat />} />
)}

Page level - until it is allowed by the feature flag, we will still use old profile page instead of the new one. From this level, since the entire page is formed as new file/component, all the logic in it are restricted automatically. This we need to do, when there is a route that users are already using.

<Route index 
    element={isProductionEnable("NewHomePage") 
        ? <HomePage /> 
        : <ExplorePage>
}/>

Component level - any logic in a component will not be processed, until allowed by the feature flag. This is the trickiest place, as it can become messy very easily. This happens, when we already have a feature being used and we are updating only some pieces of it. The right way to do this is to isolate that changing part into a tiny component (in the same file) and from there the conditional part looks as clean as the above ones.

{isProductionEnable("MultiChat") && (
    <button
        onClick={() => {
            navigate("/multi-chat");
        }}>
        <GroupChatIcon />
        <span>Start Your Multi Chat</span>
    </button>
)}

Why we store the entire Feature flag object at global level

To make the UI deterministic. Imagine calling the feature flag API many places at page or component level, then you would need to handle the loading state and you will be on risky stage on what to show at that point. But again, as every system decisions, this decision is also a trade-off. Since we are making this call first, to decide what to show or not-show to the user, it becomes initial-load-blocking in nature. And (god forbid) if this fails, we are in deep trouble. And here it all boils down to “Evaluation”. Evaluation on what type of application you are : can you do server rendering based on cookies or you need information from client first.

Feature flag system is not just about on and off switches

It is used for gradual rollouts as well. There is an “User evaluation” layer in it as well. Which based on cookie or client-sent information, decides which flags to turn on or off for a particular user. Usually, if it is a major version change, we should use things like Canary deployment, but for minor/incremental updates we can use feature flags.

The cons

Increased complexity + More code + Requires maintenance + One more failure point. For the failure point, you want your feature flag system isolated and as simple as it can be. For maintaining it (removing the flags and old codes), we can use a monthly level maintenance day, where we check if a feature has not been reported over 6 months after being used by at least 90% of user, we should remove that flag and all the old codes neighboring it. And for complexity and more code, we already talked above.

The other ways

Now there are SAASs which offers feature flag systems as paid/semi-free structure. But I believe a team should only use that, if they are at really high scale and they don’t have enough people/time to manage a separate system and the maintenance that comes with it. (btw, Next.js also has a feature flag system for the client side management)

Extras

Now while doing A/B testing, we will be showing one set of changes to one user and an alternate to that to another user. But we need to be consistent on what we are showing to a particular user, we cannot show both the A and B set of changes to the same user between simple browser refreshes. So if the user is authenticated then it is simple, in our DB we have decided the group he is in, but if the user is at a non-authenticated set, then we have less control.

And for that we use : set-cookie. On the first ever render, we will set this parameter to a key that helps our server to identify the user’s browser. And in the further requests, we send that cookie back to our server, hence ensuring the same set of features shown to a particular user every time.

Now, one of the common questions here is why cookies and why not local or session storage, it is mainly because we can set a cookie from server very easily and cookies are automatically sent to the server on every request, while local/session storage are client-only.

That’s all the 101 of Feature Flags for now, I will keep on adding about Feature Flags developments here. Thanks for reading this through 🌈