Keat: Serverless feature flags for progressive releases

Tuesday, July 5, 2022

Keat is the new kid on the block for managing features in style - it's free, open source, and you can use it to decouple your deploys from releases to deliver stable products at speed.

Out-of-the-box it’s got you covered for progressive rollouts, targeted audiences and scheduled launch days. Though what makes Keat unique are the plugins and remote configuration format which blur the lines between library and framework.

Let’s dive right into the code to explore how you can combine Keat with Cloudflare for sub-50ms latency feature flags across the globe.

Add feature flags to your application

Start by installing Keat to your codebase:

npm install keat

Afterwards you can add it to your React application. Don't worry if you prefer Vue or friends, Keat is framework agnostic and the adaptors are only there for a better developer experience.

import { keatReact, rollouts, launchDay } from "keat";

const { FeatureBoundary } = keatReact({
  features: {
    recommendations: true,
    search: 25,
    redesign: "2022-07-20",
  },
  plugins: [rollouts(), launchDay()],
});

export function App() {
  return (
    <div>
      <h1>Keat demo</h1>

      <FeatureBoundary name="redesign" fallback={<p>Your old design</p>}>
        <p>Your new design</p>
      </FeatureBoundary>
    </div>
  );
}

You can learn more about features, rules and plugins in the README, though for this demo it suffices to remember that Keat's type-safety helps reduce technical debt, and that we got three features:

  • recommendations is always enabled,
  • the rollouts plugin causes search to be enabled for 25% of users, and
  • the launchDay plugin causes redesign to be enabled starting 20th of July.

Add remote configuration with Cloudflare

Keat’s rules and plugins separate data from the code that evaluates it. The outcome? A JSON object served from anywhere is all you need to toggle your feature flags remotely.

To illustrate, it takes less than five minutes to remotely disable recommendations and increase the reach of search to half our user base by serving the following JSON object:

{
  "recommendations": false,
  "search": 50
}

First login to Cloudflare and go to the Workers overview to create a service:

Afterwards you should press quick edit:

Next you can simply follow Cloudflare’s return JSON example which only takes a dozen lines of code. Click Save and Deploy to finalise your backend.

Finally, all that’s left is to fetch the remote configuration with Keat, which takes exactly one line of code:

import { keatReact, remoteConfig, rollouts, launchDay } from "keat";

const { FeatureBoundary } = keatReact({
  features: {
    recommendations: true,
    search: 25,
    redesign: "2022-06-20",
  },
  plugins: [
    remoteConfig("https://still-paper-39c9.zagrit.workers.dev/", { interval: 60 }),
    rollouts(),
    launchDay()
  ],
});

And that’s it! You successfully decoupled deploy from release. Toggling your feature is a quick edit in the Cloudflare console and your website automatically polls it every minute.

Closing thoughts

This demo only uses a fraction of the available plugins, so make sure to check out the repository to learn about (anonymous) users, targeted audiences, multi-variates, tackling slow remote configuration with feature display, and more.

Throughout the demo you also learned that Keat only needs a JSON object. Would you deliver it differently? Maybe you’d prefer embedding it directly into your API or streaming it in real-time over WebSockets?

Join the discussion on Hackernews to let me know or dive into Keat's repo to get started yourself!

Bonus: build a custom plugin

Let's build a custom plugin that enables features during working hours. You simply need to return a JavaScript object that hooks into Keat’s lifecycle and checks whether the current time falls within the available periods.

import { Plugin, takeStrings } from "../core";

const DAYS = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"] as const;

type Day = typeof DAYS[number];
type Period = { from: number; to: number };
type Schedule = Partial<Record<Day, Period[]>>;

export const schedule = (
  name: string,
  schedule: Schedule = DEFAULT_SCHEDULE
): Plugin => {
  return {
    onEval({ variates, rules }, { setResult }) {
      const index = rules.findIndex((rule) =>
        takeStrings(rule).some((s) => {
          if (s !== name) return false;
          const now = new Date();
          const hour = now.getHours();
          const periods = schedule[DAYS[now.getDay()]] ?? [];
          return periods.some((p) => p.from <= hour && hour >= p.to);
        })
      );

      if (index !== -1) setResult(variates[index]);
    },
  };
};

const DEFAULT_SCHEDULE: Schedule = {
  monday: [{ from: 9, to: 5 }],
  tuesday: [{ from: 9, to: 5 }],
  wednesday: [{ from: 9, to: 5 }],
  thursday: [{ from: 9, to: 5 }],
  friday: [{ from: 9, to: 5 }],
};

What's with the indexes? Keat supports multi-variates for A/B/C testing and more. To do this, Keat's variates array mirrors the rules array, for example, variates ['a', 'b', 'c'] with a rule [false, true, false] will set the result to variate[1] which is 'b'. Plugin creators should take this into consideration.

All that's left is to add your custom plugin to Keat so you can monitor a risky algorithm refactor during working hours without having to worry about unexpected overtime.

import { keatReact } from "keat";

const { FeatureBoundary } = keatReact({
  features: { someRiskyAlgorithmRefactor: "businessHours" },
  plugins: [schedule("businessHours")],
});

Sign up for the newsletter and I'll email you a fresh batch of insights once every while.

✉️