22
Pac-Man Shows You the Benefits of Feature Flags
In May of 1980, twelve year old me wandered into my local arcade and I peeped a new game called Pac-Man. I usually played Space Invaders, Breakout or Flash pinball. But this was something different. Full color, lots of sound and something truly remarkable – these ghosts chasing pac-man around the board seemed to have some intelligence. It turns out that each of the 4 ghosts had a different algorithm that determined how they would chase after pac-man. It’s hard to imagine from the vantage point of today, but back then, there was just nothing like this.
This was also the year that I wrote my very first computer program in Basic on a Tandy TRS-80 (lovingly referred to as the “trash 80”). I then graduated to Commodore PET, Vic-20 and C-64. My love of programming and games came together – I was obsessed with how these arcade games worked.
Fast forward to today, and you can buy a handheld pac-man game for less than twenty bucks! And in my role as lead developer advocate, I get to help people learn all about feature flags and how they can improve the way teams build and release software.
Much like the way the ghosts make decisions to chase pac-man, with feature flags, you can make decisions about the experience your users have on the fly. Over the course of this post, you’ll see how an in-browser version of pac-man can exhibit different behavior based on how feature flags are set. You’ll also witness these changes take place before your very eyes in real time, all without having to update or redeploy the code.
If you’re a developer, you’ve likely had this experience: product prioritizes a new feature. You and your team build it. All of your tests pass. QA gives the “thumbs up”. The deploy to production is a success. And then, something unexpected happens. Maybe there’s some weird new latency in the site. Maybe you’re getting support calls about something that used to work not working right anymore. The team leaps into action – do you work on a hotfix or revert to the previous version or both? These options take time and could introduce dreaded downtime.
What if you could release a new feature to production, but only give a certain subset of users access to the new feature? For all your other users, nothing would be different. What if when you ran into a problem, you could immediately kill the new feature without any disruption to your application? What if you could ramp up your new feature in a controlled way until 100% of your users had access to it, all the while confident that if something changed in the health of your application you’d be notified and could scale back or kill the feature? This is what feature flagging is all about. It’s a way to externalize and decouple the rollout of features.
If all you need are decision points that express something like: “if this condition is true, show this bit of the user interface and if not, show this bit of the user interface”, you could probably just build that into your code. As developers, we know that these sorts of forays into: “I’ll just build it” often end up taking up a disproportionate amount of programming time. All you wanted to do was build the next great travel booking website and now you’re spending half your sprints working on a framework to make the development lifecycle more efficient.
Split is a platform that expands on simple feature flagging by bringing in a flotilla of features to support canary releases (a way to safely test new features in production), kill switches (immediate and global turn-off of features), sophisticated data gathering and monitoring and complex decision making based on free-form and fully configurable attributes.
On top of all that, Split’s asynchronous streaming architecture makes feature flag changes nearly instantaneous for users around the globe.
The code for this post can be found on the pac-split-man GitHub repo.
I like to see an example in action right out of the gate, so let’s fire it up! The only requirement to run the application locally is node.js. To get the application running, execute the following:
npm install
npm start
In your browser, navigate to http://localhost:3000
and – boom! – there’s good ole pac-man running in your browser.
Press n
to start a new game. Press s
to toggle the sound on and off. Use the arrows keys to move pac-man around.
In case you’ve never played pac-man – we need to have a talk! But, seriously, your job is to avoid the ghosts and eat the dots. In each of the four corners you see slightly larger dots. When you eat these, the ghosts will change to a dark blue color and you can chase after them and eat them (but only for a short time).
If you’re a super nerd like me, you may notice that the ghosts don’t move according to their algorithms from the original game. In the arcade game version, each ghost had its own specific rules for how it would move to chase pac-man (here is a great post on the original ghost algorithms). That being said, each ghost would use pac-man’s current position to make a decision on where to move next. In this browser version, the ghosts’ default movement is very simple: pseudo-random.
Let’s imagine you’ve released this browser version of pac-man to the world. You get some feedback that the game is much too easy with the ghosts moving about randomly. You’d like to release an update that gives the ghosts “radar”. That is, the ghosts will know pac-man’s current location and will take that into consideration when they decide how to move next.
It shouldn’t be too hard to implement this new behavior. But, you’ve been burned before by the seemingly easy. So, this time, you’re going to put this behavior behind a feature flag so that you can be in complete control of it. Maybe, to begin with, only a certain subset of your users will get the radar mode so that you can get their feedback before enabling it for everyone. And, if something goes very wrong with this new feature, you can turn it off globally with the click of a button.
In order to build feature flags into pac-man, you first need to set up Split. You’ll do that next.
Split offers a free developer tier that includes a 30-day trial of premium features. To start, create a split account here.
You’ll get an email with an activation link. Click the link and set your password.
Once at your Split dashboard, it’s time to copy an api key and create your first feature flag. It’s ok if you don’t know exactly what’s going on yet – all will be explained below.
Click the grey square in the upper left. This square always shows your current workspace in Split and you can have more than one. If this is a new account, you’ll be working with the default workspace allocated for you and you will see that the grey square in the upper left shows DE. From the menu that pops-up, choose Admin Settings toward the bottom. Then click API keys.
Want to jump ahead? Copy the first API key in the list (which should be type: Client-side and environment: Staging-Default ). Save the value of this key for the next steps.
Interested in learning more about what these keys are all about? Keep reading!
API keys are used to configure your applications to communicate with Split. A new Split account will have four API keys automatically allocated. You can always create more of your own. There are two keys in each of two environments: Staging-Default and Prod-Default. Each key in each of these environments is either a Client-side or Server-side. Single Page Apps (SPA), mobile and desktop apps are all naturally insecure. That is, you don’t have control over those environments. Someone on a compromised device might be running your application that speaks to Split. As such, Client-side API keys will have less privileges. On the other hand, applications that you deploy to your own servers, like .NET, Spring Boot, and Node.js are under your control. Therefore it’s safe to have an API with more privileges. That’s what the Server-side API keys are for.
Since the example in this post is a JavaScript app running in the browser, we want to make use of a Client-Side type API key.
On the left-hand side of your Split dashboard toward the middle, click Splits. The click Create split. Enter: PacMan_RadarGhost for the Name and select the default of user for Traffic Type. Click Create.
NOTE: The code example used in this post looks for a feature flag named PacMan_RadarGhost , so it’s important to use that exact name.
A Split (synonymous with a feature flag) is not very useful without some rules. And, the rules you define are bound to an environment. So, make sure that Staging-Default is selected in the Environment dropdown. Click Add Rules. Notice that under the Define treatments section, there are two defaults: on and off. We’ll stick with those for our pac-man application. Scroll down and you can see that the value in the Set the default rule section is off. This is good for us as well. It means that the “new feature” will not be served by default. Click Save changes. The next thing you see is a confirmation screen. This shows a git-like diff of the configuration changes you’re about to save. Click Confirm.
Everything you need to have the pac-man app interact with split is now set. Leave your Split admin dashboard where it is – you’ll return to it later.
If you left the pac-man app running, exit it now with ctrl+c
. Next, restart it with the client-side api key you copied from Split:
SPLIT_AUTH_KEY=<your client-side key> npm start
Initially, nothing will be different. I recommend you partition your screen with your Split admin dashboard on the right and the pac-man game on the left as pictured below:
Start the game again by hitting the n
key. The ghosts are moving around randomly as before. Now, it’s time for some split magic. Hit the p
key to pause the game. Next, change the default treatment from off to on. Click Save and then Confirm.
Did you notice the change on the left? Nearly instantaneously, the status bar on the bottom has changed from GHOSTS: CHILL to GHOSTS: RADAR. This demonstrates one of the important pillars of all of Split’s SDKs: a behind-the-scenes streaming architecture that propagates changes from your feature flag definitions in milliseconds. Hit p
again to continue the game. But – you better run! You’ll notice that the ghosts are now hyper focused on your location and their closing in fast.
You can set the default treatment back to off again and you’ll see that the status bar returns to GHOSTS: CHILL mode and the ghosts are moving about randomly again.
In the next section, we’ll take a deeper look at the code that makes all this happen.
Here, we return to the code found on the pac-split-man GitHub repo.
This example is purposely organized as a vanilla JavaScript project. It has only one real dependency, which is a CDN reference to the Split JavaScript SDK. While this is not an efficient, modern approach to a JavaScript project, it does keep things simple for the purposes of demonstration.
Take a look at the index.html
file – the entry point for the app.
<div id="pacman"></div>
<script src="https://cdn.split.io/sdk/split-10.15.4.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.8.3/modernizr.min.js"></script>
<script type="module" src="main.js"></script>
The div
is where the pac-man game will be rendered. The first script
tag gets the Split JavaScript SDK. The second script
tag gets a library called modernizr, which helps to identify features of different browsers. The last script
tag loads the main JavaScript file. Note that is uses module
type so that the pac-man app can use a modern modular approach to JavaScript using ECMAScript 2015 (short name ES6 as it’s the sixth edition of ECMAScript).
We won’t go into all the details of the mechanics of the game itself, but we will dig into how it’s connected to Split. All of the setup and interaction with Split happens in main.js
.
First, the Split configuration object is read in with this line:
import { SplitConfig } from './split_config.js';
It contains an authorizationKey
as well as a key
. The authorizationKey
is the client-side API key you copied earlier and was written to this file when you started the app above.
The key
identifier can be a little confusing at first. This is a free form value that can represent a unique identifier of your choice. It is often an email address or other user id. Split leaves it up to you on purpose so that it can be very flexible. In this demo, we’re not using any sort of authentication or user identifier, so it’s just hard-coded to ANONYMOUS
.
With the SplitConfig
available now, we set up the Split client:
var factory = splitio(SplitConfig);
var splitClient = factory.client();
Next are some very simple but powerful blocks of code to react to events coming from Split:
splitClient.on(splitClient.Event.SDK_READY, function () {
handleTreatments();
console.log('Split is ready!');
});
splitClient.on(splitClient.Event.SDK_UPDATE, function () {
handleTreatments();
console.log('The SDK has been updated!');
});
This demonstrates the event-driven nature of the Split JavaScript SDK. We’re capturing two events: SDK_READY
(when the sdk is ready to interact with Split) and SDK_UPDATE
(when any change is made to treatments defined in Split). In both cases, the function handleTreatments()
is called:
function handleTreatments() {
var treatments = splitClient.getTreatments(['PacMan_RadarGhost', 'PacMan_SuperPac']);
el.dispatchEvent(new CustomEvent('splitChange', { detail: treatments }));
}
The handleTreatments()
function obtains the values of the named treatments from Split. For this post, the one we care about is: PacMan_RadarGhost
. Notice that it takes the collection of treatments returned from Split and fires a custom event called splitChange
. This simple but powerful line enables us to decouple the rest of the application from Split. The Pac-Man game has a listener for this custom event and will react to the treatments
object passed into it.
Take a look at the event handler in game.js
:
wrapper.addEventListener('splitChange', function(e) {
console.log(e.detail);
console.log('detected split change');
if (e.detail['PacMan_RadarGhost']) {
let ghostMode = Ghost.CHILL;
let userForGhost = undefined;
if (e.detail['PacMan_RadarGhost'] === 'on') {
ghostMode = Ghost.RADAR;
userForGhost = user;
}
for (let i = 0; i < ghosts.length; i++) {
ghosts[i].setMode(ghostMode);
ghosts[i].setUser(userForGhost);
}
}
});
There’s a lot going on here, so let’s break it down. First, we check to see if PacMan_RadarGhost
is in the details passed in to the event handler. If so, we check to see if the value of PacMan_RadarGhost
is set to on
. If so, the ghostMode
is set to Ghost.RADAR
. This is what changes the status to RADAR
in the game. Finally, we iterate over the list of ghosts and set the mode accordingly. The user object is also passed into each of the ghosts so that they can know pac-man’s position on the screen when in RADAR
mode.
The last thing to look at is the move
function in ghost.js
:
function move(ctx) {
if (mode === Ghost.CHILL) {
return moveChill(ctx);
} else if (mode === Ghost.RADAR) {
return moveRadar(ctx);
}
};
There are two ways a ghost can move: randomly (CHILL) or purposely toward the pac-man (RADAR). Notice that there is no mention of Split at this point – the code is completely decoupled from Split and treatments.
Apps are no fun unless we can share them with the world! On the Deploying section of the GitHub repo for this post, you may have noticed the friendly purple Deploy to Heroku
button. At Split, we <3
Heroku for rapid deployment, testing and hosting. They have a generous free tier that you can sign up for here. If you click the purple button (and, you’re logged in to your Heroku account), you’re taken to a page that asks two simple questions in an input form: what would you like to call the app? And, what is your client-side API key for Split?
Fill in this information and Click Deploy app. In about twenty seconds, your own instance of the pac-man app is now publicly available. Pretty cool, right? In case you’re interested, it’s the app.json
file in the project that drives this easy deployment.
In this post, you got a first taste of the power of Split and feature flags. There’s so much more to Split! Data gathering and metrics, automated actions such triggering a kill switch on a feature flag are some of the many facets of the Split offering. Stay tuned as I expand on and add more functionality to the Pac-Man game.
Ready to learn more? We’ve got some additional resources covering all of these topics and more! Check out:
22