Solving Feature Flags on the Front End

11 March 2019 by Appknobs

Feature flags - also known as feature toggles, feature bits or "flips" - are quickly becoming the norm when it comes to application development. They allow teams to speed up code-to-production workflows, boost confidence by validating changes rapidly, in production and eliminate convoluted branching strategies - amongst other things.

As with any complex problem in software engineering, there are many ways to solve feature flags. As client-side applications grow in complexity and take over more and more responsibility in terms of business logic, there is an emerging trend to move all or most feature toggles into the UI. Introducing toggles allows the client-side app to keep changes hidden from general customers until it is deemed ready, but still enable complete development-testing-deployment cycles to take place. At the same time supporting back-end services can grow and improve at their velocity, as they don't need to be timed or constrained by front-end visibility concerns.

Implementing a robust, reliable and high-value feature flag solution is a serious undertaking. Adding feature switching with a bespoke solution to already highly complex front-end projects can be frustrating. Teams usually face unexpected problems, introduce compromises in code quality and spend more time on implementation than expected — precisely the opposite of the clarity and increase in productivity that feature-flags promise.

Our core values at Appknobs.io are clarity, simplicity and the will to provide a great developer experience (DX). We also know that front-end is hard and modern applications have specific challenges that might not be obvious to engineers focusing on other aspects of development.

We have started with a declarative, expressive API in mind - we wanted to solve the last mile well. What our users expect is a clean, declarative solution that is easy to add and also easy to remove. The @appknobs/react library provides an idiomatic and modern implementation that is easy to understand, expressive and follows best practices.

Let's look at how you can implement UI focused feature flagging using Appknobs.io.

The Booking App example

Before and after

In this example, we imagine a hotel booking app where a new feature is under development: the ability to rent a car for the duration of your stay. This service takes advantage of a third party service and introduces a new user journey. Other improvements take place in parallel. Naturally, the new feature cannot be released until it's feature-complete.

What would be the challenges in such a case?

Long development time

Since both the UI and the back-end services need to be extended, this feature takes more than a couple of days. Adding and testing new visual components, implementing a new user journey, creating supporting back-end services, adding validation rules, extending the data storage - and so much more - all take time. The team might opt for a couple of long-lived branches where the changes and the new code lives. Parallel branches unfortunately almost certainly lead to a "merge hell". As independent changes and improvements appear in the codebase, the production (trunk) and the car-hire branch (or branches) diverge more every day. The situation becomes worse as time passes.

Validation and feature testing

In scenarios where integration happens last minute, high-level feature testing is a real headache. The first question is: where can you test the new feature? If it lives outside the main (deployment) codebase during development, it most likely won't be deployed to the "usual" environments. Setting up special, dedicated environments might be possible, but it can be, and the burden on developers be high. The chance of discrepancies between environments is high. As an extra burden, the same problems that make long-life branches problematic plague the test code too: as features change in the "production" version, test code also changes. You'll face the same issues as with maintaining the core product code.

Delivery

When the team is ready to release the new feature as a significant change from a dedicated branch, all sorts of surprises greet them. Subtle mismatches break the app; a missing configuration stops it from working. Small, forgotten tasks cause headaches and delays. Firefighting ensues for days. It might be difficult or virtually impossible to roll back or turn the broken feature off.

Most of these problems can be eliminated entirely or at least minimised by daily integration to the main branch (Continuous Integration - CI). CI allows the team to introduce meaningful but simple-enough improvements that are easy to understand and can be safely integrated. From the integrated codebase your organisation can build up the Continuous Delivery (CD) pipeline, then after gaining confidence, do Continuous Deployment to production.

The obvious question now is: how do you add new features to your application without actually making them available to the public? How do you hide new features in plain sight?

You're probably not surprised to learn that feature flags can elegantly solve these problems. Let's look at some React code examples using @appknobs/react! To learn about all the front-end libraries and runtimes we support, have a look at @appknobs/client.

Get started on Appknobs.io

To get started, you need to obtain an API key and register your app at https://console.appknobs.io.

First, register a user at https://console.appknobs.io/register.

Next, visit https://console.appknobs.io/project and click "Add app manually" then "Copy appId".

Adding an app

App information

Finally, visit https://console.appknobs.io/apikey and copy your API key.

Getting the API key

What's left is to grab our UI and client libraries:

npm install @appknobs/react @appknobs/client

Implement a Feature

Now identify all UI elements to hide while the feature is deemed incomplete. You'll mark these "entry points" as Features so the Appknobs services can control their visibility. In the current - admittedly simple - example you find one such entry point: the "Rent a Car" button. There could be several others: menu items, promotion widgets, footer links and so on. No matter how many there are, you'd deal with them the same way.

Marking a UI component as Feature is simple:

// BookingPage.jsx
import {Feature} from '@appknobs/react'

export const BookingPage = () => (
  <>
    <h1>Reserve you Room</h1>
      /* ... */
    <Feature name='rent-a-car'>
      <RentACarButton onClick={startRentACarJourney} />
    </Feature>
  </>
)

Note: for brevity, we're only showing the relevant parts

As you can see, the <Feature> component was used to indicate that there is a "Rent a Car" feature present in our app, and anything wrapped inside is only visible for developers (to be completely precise, it won't be visible to anyone until we assign at least one condition to it - see below).

In real life applications, there are usually a handful of <Feature> components present. To coordinate their behaviour, you need to introduce a high-level wrapper to your App:

// App.jsx
import {Appknobs} from '@appknobs/react'
import {newBrowserClient} from '@appknobs/client'

const client = newBrowserClient({apiKey: 'YOUR_API_KEY', appId: 'YOUR_APP_ID'})

export const App = () => (
  <Provider ...>
      <Appknobs client={client} payload={{email: user.email}}>
            <Router .../>
      </Appknobs>
  </Provider>
)

Let's understand what's happening here.

The <Appknobs> wrapper ensures all your <Features> have access to the up-to-date feature visibility list; in other words the application instance configuration. This way, you don't need to worry about passing down special props or conveying the visibility to the Feature in any other way.

The client property passed to Appknobs needs little explanation. The client is an implementation that handles all the complexity of connecting to the Appknobs services, issuing the correct API calls and fetching the feature list for the current instance. The only thing to keep in mind is to pick the client appropriate to the runtime - be it Server-Side Rendering (SSR) in Node, a browser, a React Native or Electron app.

The payload property is a description of your runtime, and the Appknobs services use this information to decide if any of the <Features> present in your app should be enabled. You define the rules for making this decision on the Appknobs Web Console,

The payload is a plain JavaScript object without formal restrictions. You are welcome to pass any information that can help you decide if an application instance (or in other words, a user) should have access to certain features or not. The example above uses a key named email, but you can include any relevant information, for example, the hostname (good for stage-based decisions), the username, the current timestamp, the name of the group the user is part of, the user's favourite colour...

A typical use case is to send the user's email address and set up a rule that evaluates to true if it matches a pattern. To see how you can do that, let's visit console.appknobs.io.

Adding conditions

You have already created an application, and now you need to add a Feature to it. Adding features can be automatically done using @appknobs/cil, or you can manually add one:

First, visit https://console.appknobs.io/project and click "Edit Features" under the name of your app:

No features defined yet

Next, click "Add feature manually" then click "Edit conditions":

Adding a feature

On the - empty - conditions screen, click "Add condition":

No conditions yet

Next, fill out the form and save:

Adding a condition

Successfully added a condition

If you reloaded your app, the feature named rent-a-car would be visible to the users you've selected but no to anyone else.

You're done

Congratulations, you've implemented a fully working, production-ready feature-flag solution!

Going through these simple steps unlocked some powerful features that help you sidestep all the problems associated with slow integration and long release cycles.

Once you hide a part of your application behind a <Feature> toggle, you can decide if you want to see the latest-and-greatest version or the public one.

You have eliminated merge-hell as both the front-end and the back-end teams can keep deploying their changes.

The UI team can integrate their bite-sized changes whenever they are ready. Customers won't be able to see them as they live behind a flag for now.

Since changes are - or can be - integrated daily, the public "release" and the codebase with the new "Rent-a-Car" feature are not diverging. They are the same all the time!

You have enabled quick feedback: developers can set up flags so stakeholders or select clients can evaluate the latest changes as soon as they are ready. Rapid feedback leads to higher product quality.

3rd party access: features can be opened up - even temporarily - to 3rd parties so they can visit the new flow and help debug integration issues. Being able to open up parts of your application can be a huge relief and time saver.

Feature tests can be written and run as soon as parts of the new feature are enabled. A standard solution is to have specific test users that get the new feature based on their name/email or the server hostname.

You discover integration issues before they can cause embarrassment or monetary loss: QA can verify new features in the production environment before anyone else has access to it.

Turn off: in case there are business- or technical reasons to roll back a feature, management can turn it off in an instant.

Booking form source: colorlib.com/etc/bforms/colorlib-booking-18/

Appknobs is feature flags without the work
Read our blog, Twitter and newsletter for tips, updates, tutorials and articles.