Highrise has been around since 2007 and we have many loyal customers who have stuck with us from the beginning. So you can imagine how delicate it can be to change something. We try our hardest to improve the product without interrupting current product flow, but we can’t always predict the impact of a change we make. There’s an unbelievable number of creative ways our customers use Highrise. But we try.
One thing Michael Dwan added to our bag of tricks is a Feature Flagging library we use on everything we can. Feature Flagging is the process of wrapping a new feature or change in logic that turns the feature on or off given some data on the user or the account accessing the app.
It’s a great way to test acceptance of something before everyone gets it. And can also be used for features that are difficult to test in environments other than Production. Maybe you aren’t completely sure what the performance impact might be, but you have a tough time replicating the performance load in development or staging. A slow rollout using flags might help soften the deployment as you measure the impact.
Here’s a basic setup below that we used for awhile. It did the trick. We have a fancier tool now called Bellhop (more on that later) that we use to turn things on and off in our app without redeploying anything.
But before you can run you should walk and we thought this might be worth sharing… Here’s the Feature Flags library we used. I’ll break out what each thing does below.
https://gist.github.com/n8/509f000552291c1943fe5d87018bae81
BETA_ACCOUNTS are subdomains of accounts that always get our new features turned on.
FLAGS is a hash of feature names. They list percentages (as integers: 10% = 10, 66% = 66) of accounts that should have the feature on like “10% of accounts should get this” or they list explicit account subdomains that should get the feature.
The real magic is here:
return true if Zlib.crc32(“#{feature}:#{account.id}”) % 100 < config[:percentage]
What’s up with that?
We want our features to get rolled out to percentages of people. So we combine their account’s id with the name of the feature and turn it into an integer. The Zlib library has a crc32 utility that will convert our combined string into an integer checksum that’s constant for that string.
Since these integers should be fairly randomly distributed they should also have their last two digits evenly distributed between 0 and 99.
In other words, 10% of our account ids + feature names will end with a digit between 00–10, 20% of our account ids + feature names will end with 00–20, 30% 00–30, you get the idea. All we have to do is look at the last two digits of an (account ids + feature name converted to an integer) and compare it with the percentage of accounts that should get this feature.
If we just used account id’s and didn’t include a feature name, accounts would always get pinned to the bucket their last two digits were in. An account that ended in xxxxx01 would always end up in the 10% rollout group and would always see possibly experimental features. Adding the feature name randomizes the bucket these folks fall into. (Thanks for the feedback @michaelmelanson! He caught the error in an original version of this post.)
And there’s an easy way to get the last two digits of any number. Just “mod” it by 100.
I won’t go into an in depth discussion about what a modulo operation is. It’s really just the remainder of a number when it’s divided by another number.
In Ruby the modulus operator is the percent sign. So 5 % 2, is the remainder when you divide 2 into 5. 2 goes into 5 two whole times, and then there is a remainder of 1. In other words:
5/2 = 2.5
The remainder or modulo is 2.5’s decimal * 2 = 0.5 * 2 = 1
So 5 % 2 = 1
Well let’s look at what happens when we divide numbers by 100.
9837364/100 = 98373.64
0.64 * 100 = 64
64 is the modulus
Ah, the modulus of a number with 100 gets us the last two digits of that number.
So in pseudo code, here’s what we’re doing:
if last two digits of (feature name + account id converted to an integer) < percentage
they they get the feature
And so using our library we’ve been able to add blocks of code like this around important things we don’t want everyone to have right away:
if FeatureFlags.enabled_for_account?(account, :cool_new_thing)
# do something new and cool
end
Need more people to get your cool feature? Just up the percentage in your flags to a higher number and redeploy your code.
Hope this helps if you need a quick and dirty library to start doing some feature flagging. Stay tuned though. We’ve got a really nice system we use today that we’ll open source soon that makes this much more flexible.
P.S. It would be awesome to meet you on Twitter, and check out what we’re up to at Highrise. Lots of great open source projects from us on their way.