This is part two of the series. For this to make sense, I recommend you first read / complete part one.
Actually Useful Feature Flags
Feature flags as demonstrated in part one of this tutorial are not actually very useful. Yes, they abstract the values to an external storage system, and that is nice.
But the real value of feature flags is when you can combine runtime information and / or environmental and / or client information to adjust the value of the flag in realtime.
An Example
In the previous example, the possible flag values were:
-
#FFFFFF(white) ifflags.jsondid not exist -
#73A53E(green) because that was thedefaultVariant
However, what if we wanted a ruleset like this:
-
#FFFFFF(white) ifflags.jsondoes not exist -
#4AB9D9(blue) ifflags.jsondoes exist and the user is using the Google Chrome browser. -
#73A53E(green) ifflags.jsondoes exist and the user is using the Safari browser -
#FF0000(red) ifflags.jsondoes exist and the user is using any other browser (ie. not Chrome or Safari)
We could, of course, code something like this:
// Assume req is the request object
const userAgent = req.headers['user-agent'];
if (req.headers['user-agent'].includes("Chrome")) {
const backgroundColour = "#4AB9D9"; // blue
}
else if (req.headers['user-agent'].includes("Safari")) {
const backgroundColour = "#73A53E"; // green
}
else {
const backgroundColour = "#FF0000"; // red
}
But that is:
- Missing the advantages of using a feature flag system
- Hardcoded
- Messy
Let the Flag System Perform That Logic
Instead, let flagd perform that logic for you.
New Flag Definition
First, overwrite your existing flags.json file with this content:
{
"$schema": "https://flagd.dev/schema/v0/flags.json",
"flags": {
"background-colour": {
"state": "ENABLED",
"variants": {
"white": "#D6D4D2",
"green": "#73A53E",
"orange": "#FF7C00",
"lime": "#D3D309",
"blue": "#4AB9D9",
"red": "#FF0000"
},
"defaultVariant": "red",
"targeting": {
"if": [
{
"in": [
"Chrome",
{
"var": "uA"
}
]
},
"blue",
{
"if": [
{
"in": [
"Safari",
{
"var": "uA"
}
]
},
"green"
]
}
]
}
}
}
}
You can read that as:
- One flag called
background-colouris available and enabled. - It has
6possible values: white, green, orange, lime, blue and red. - By default, the flag variant will be
redand thus return a value of#FF0000. - When the flag is evaluated, if some additional contextual data with the key of
uAis present and the value ofuAcontains the wordChromethen returnblue, notred. - When the flag is evaluated, if
uAis present andChromeis not present butSafariis, returngreen, notred. - In all other cases (
uAis not present oruAis present but contains neitherChromeorSafari), return the default:red.
Here you can see that, with relatively simple JSON Logic, flagd can build complex rules. flagd can perform lots of other logic and the flagd playground is a great place to experiment in realtime.
New app.js
Now overwrite your app.js file to this:
var fs = require('fs');
var http = require('http');
var port = 9123;
var html = fs.readFileSync('index.html').toString();
var {OpenFeature} = require("@openfeature/server-sdk")
var {FlagdProvider} = require("@openfeature/flagd-provider")
var getBackgroundColour = async function(req) {
const userAgent = req.headers['user-agent'];
const backgroundColour = await client.getStringValue("background-colour", "#FFFFFF", { 'uA': userAgent });
return backgroundColour;
}
// OpenFeature Initialisation Code
// Register your feature flag provider
OpenFeature.setProvider(new FlagdProvider());
// create a new client
const client = OpenFeature.getClient();
// =======================================================
// This is our main HttpServer Handler
// =======================================================
var server = http.createServer(async function (req, res) {
res.writeHead(200, 'OK', {'Content-Type': 'text/html'});
// replace background colour
var finalHtml = html.replace(/BACKGROUND-COLOUR/g, await getBackgroundColour(req));
res.write(finalHtml);
res.end();
});
// Listen on port 9123, IP defaults to 127.0.0.1
server.listen(port);
// Put a friendly message on the terminal
console.log('Server running at http://127.0.0.1:' + port + '/');
console.log("Info", "Service is up and running - feed me with data!");
Open http://127.0.0.1:9123 in Google Chrome and then again in Safari.
Explanation
Lots of the content is the same as part one, but let's run through things.
First look at app.js. Again we initialised OpenFeature and the flagd provider.
This time though, when we request the background colour, we pass the req (request) object into getBackgroundColour.
The User-Agent string is extracted from the req object:
const userAgent = req.headers['user-agent'];
The "user agent" is sent to flagd with a key of uA (you can pass any values you wish to flagd - we just chose to use uA in an attempt to be clear that we choose the key).
If flagd is not available (because we're using in-process this would mean the flags.json file is not available), then the value #FFFFFF would be returned.
flagd performs the logic (which you saw is specified in the flags.json file) and returns the correct value back to the OpenFeature client.
Summary
So, now you know how to use contextual runtime information with OpenFeature and flagd.
Whether you're using flagd or any other OpenFeature provider, the client code is identical.
You are limited only by your imagination: User agents, browser versions, timestamps, geolocations, semver release versions and anything else you wish to use as a contextual attribute.
As long as your chosen backend can deal with the data you send, you can achieve the same as the demo above.
Happy feature flagging! 🎉


Top comments (0)