Digital Defiant Studios

Published: 2016-12-06 00:00:00 -0800

Feature flipping with environment variables

I wouldn’t call it a “hot new thing”, as it has been around for some time, but I think the idea of “feature flipping” is still considered part of the software development 2.0 (3.0? 4.0?) progression of continuous integration.

In case you aren’t familiar, the idea behind it is simple enough: you bake features, complete or otherwise, into your code that moves through the CI cycle, right into production. Yes, you heard that right - you write incomplete or undecided software and put it in production!

At the risk of abusing the term, I think that is actually an important aspect worth noting. While the idea of the software in question being incomplete is far from a valid core definition, it does highlight some of the perceptions that go along with this strategy.

These perceptions include: push often and fast, have a CI pipeline to automate the testing, automate validation and promotion of code across development tiers (a dev, staging and of course prod server, along with any replication or other high availability concerns). I would even throw in A/B(X/Y/Z) testing in here as well, because this is often an important strategy in large scale applications that have a quick product development lifecycle.

So, assuming you agree with the above, let’s get past the actual definition and into the core thesis of this article, namely implementation.

I apologize in advance for the long-winded pretext as I’m now going to introduce three words that effectively sum up my proposition:

Use. Environment. Variables.

But wait, there’s more!

Environment variables are fantastic for modern development. I’ve switched to using them for just about any data related to configuration management in my day-to-day development workflow.

This has been heavily influenced by the www.12factor.net manifesto, and one of the core tenets is to store configuration in env-vars.

I have used this model successfully to write cross-functional configuration for a completely disparate set of tools that were out of my control (at work): Pivotal Cloud Foundry, a Pivotal Labs Platform-as-a-Service (ala Heroku, et al), Puppet, a ruby based DSL and distributed service for config management, and of course plain ol' local development. All without changing much, if anything. All env-vars.

Now, back to feature-flipping.

I’ve seen this done one way in the past, and it worked pretty well. When I worked at https://www.zulily.com features were toggled on in php if blocks, using a series of helper functions. This of course made for quite ugly code, but it worked. Behind the scenes this was actually persisted in a Redis key/value store, and an administration panel was created for flipping them off/on in real time.

However, this creates quite a lot of infrastructure headaches, and it does not work at a tier level, unless you persist based on differing keys (namespaced keys, with a prefix of some sort), or you add more Redis instances for different tiers, and target them differently depending on the hostname. Quite a lot of ballooning infrastructure cost and maintenance, if you ask me.

So, this is just one example that I have from personal experience. There are probably many other ways to do this. Local files, other data stores, network requests to some kind of feature “registry”, etc… ultimately though, these are all the same, in that they defer to a protocol and service.

But what if instead, we just used env vars? Let’s just work it out. All code below will be in Python unless otherwise noted.

import os

FEATURE_A_ENABLED = bool(os.getenv('FEATURE_A_ENABLED', False))

if FEATURE_A_ENABLED:
    do_something()

Then, with our config management, we can simply inject the variable automatically:

SERVICE_LEVEL = os.getenv('SERVICE_LEVEL', 'DEV')

if SERVICE_LEVEL in ['STAGE', 'PROD']:
    os.environment['FEATURE_A_ENABLED'] = False
else:
    os.environment['FEATURE_A_ENABLED'] = True

Granted, the above example is contrived, you can imagine your config management software of choice handling this.

This would give us feature_a in dev only, but not in stage or prod. Conceptually, you can make it more granular if you like, say, to certain hostnames, or certain regions, etc… but really the power is in your hands.

Hopefully this helps in your development and release workflow!