Rails Feature Flags: Now So Flippin' Easy You Won't Believe It
As I said in the title, getting started with feature flags in your Rails apps has never been easier. With the latest release of Flipper (0.21), the golden path is all handled by simply bundling the gems you need.
If this version had a name, it would probably be convention over configuration. I'm going to take you through all the automatic ✨ now. So read on and enjoy.
Automatic Adapters
In previous versions of Flipper, you had several decisions to make to get started:
- Which adapter should I use? What even is an adapter?
- Should I include memoization? How do I do that?
- Should I preload all features, some features or no none?
No more. After more than a decade of using feature flags and eight years since the inception of Flipper, we have opinions on a default setup.
And this release makes all those choices for you.
First, drop this in your Gemfile and run bundle:
gem "flipper"
Now peel out. Yep, that's it. You are all setup and ready to sprinkle your code with power ✨. Flipper.enabled?
and friends will just work™.
How you say? What is this dark ruby-licious magic? Wherever, whenever possible (like Shakira), Flipper now tries to do all the configuration for you (#502).
Now don't get too excited. That alone won't get you very far.
But it will automatically set you up with the memory adapter so you can kick the tires and get a feel for Flipper.
Choose Your Own Adventure
To get real work done, you have to pick from our vast array of adapters or roll your own. And that is where it starts to get hard, right? Wrong.
Oooooh hashtag burn. See how I set you up there. Sorry, I'll stop slinging the hash tags and just show you.
Let's say you pick redis to store your feature flag data:
gem "flipper-redis"
With that tiny addition (literally -redis
) and another bundle
, you are ready to rock and roll feature flags all the way to production.
It's the same story for many of our other supported adapters.
Active Record
gem "flipper-active_record
# create migration and migrate
$ rails g flipper:active_record && rails db:migrate
Sequel
gem 'flipper-sequel'
irb> require 'generators/flipper/templates/sequel_migration'
irb> CreateFlipperTablesSequel.new(Sequel::Model.db).up
Mongo
# set MONGO_URL
gem 'flipper-mongo'
Need other storage? Check out the full list of adapters or roll your own.
Automatic Memoization & Preloading
The great thing is we didn't only configure clients and connections for you. If you're using Rails, we'll now set you up with everything else you need for performant feature flags.
Flipper has long included memoization of feature data per request and preloading of that data (if you so desire). But to use either you had to add the memoizer middleware and configure it correctly.
Now, Flipper comes standard with a railtie that automatically turns on memoization and preloading. One less middleware to think about post-install. And a whole lot fewer network calls by default 📉.
The important thing about defaults is making them easy to change. So we did that too.
Tweaking Memoization
If you know what you are doing, you can disable memoization entirely:
Rails.application.configure do
config.flipper.memoize = false
end
Or, just disable it for particular requests:
Rails.application.configure do
config.flipper.memoize = ->(request) {
!request.path.start_with?("/assets")
}
end
Tweaking Preloading
You can preload only particular features (say the ones used on most requests):
Rails.application.configure do
config.flipper.preload = [:stats, :search, :some_feature]
end
Or, you can disabling preloading entirely:
Rails.application.configure do
config.flipper.preload = false
end
Again, all you've done is include one gem. And now you have feature flags that do only one network call on each request. No effort for you.
Automatic flipper_id
One of the minor annoyances has always been remembering to define flipper_id
on any object that you'd like to enable or disable an actor for.
For this, Flipper now ships with Flipper::Identifier
(source). Include this module in any object and you get a sweet default implementation of flipper_id
.
class User
include Flipper::Identifier
attr_reader :id
def initialize(id)
@id = id
end
end
User.new(1).flipper_id # => "User;1"
We also automatically include Flipper::Identifier
for any Active Record or Sequel models (#505).
irb(main)> Feature.new(id: 1).flipper_id
=> "Feature;1"
irb(main)> Project.new(id: 1).flipper_id
=> "Project;1"
irb(main)> Organization.new(id: 1).flipper_id
=> "Organization;1"
We didn't want to litter the entire object space. But we thought at least these two models would be good additions.
Again, convention over configuration. If we can make a decision for you based on a best practice, then we will.
Automatic Cloud
I don't think that setup was much of a barrier to entry for Cloud.
What I'm hearing is current Flipper users (OSS) already have work arounds for some of the current problems Cloud solves. Or, they're concerned we'll impact their availability (hint: we won't, watch here and/or read on).
Regardless, while we were on this reduce friction mission, we made Cloud more automatic as well.
Superfluous Setting Removal
First, sticking with the theme of this release, we removed a superfluous setting (#511). If you have sync_method
or FLIPPER_CLOUD_SYNC_METHOD
configured, just ☠️ them.
FLIPPER_CLOUD_SYNC_SECRET
now automatically switches Cloud's sync_method
to :webhook
negating the need for sync_method
shennanigans.
Automatic Configure
Since we had automatic configuration of the bundled adapters, it seemed only right to make cloud automatically configure as well (#506).
Let's say you want to use Active Record for Flipper reads in your app with Cloud as the source of truth. Here you go:
gem "flipper-active_record"
$ rails g flipper:active_record && rails db:migrate
gem "flipper-cloud"
# set FLIPPER_CLOUD_TOKEN
That's it. Add the gems. Generate the tables for Active Record. Set the environment variable for Cloud. Done.
You want to use Redis for feature flag reads all controlled by Cloud?
No problem:
gem "flipper-redis"
gem "flipper-cloud"
# set FLIPPER_CLOUD_TOKEN
Whatever adapter you pick, Cloud automatically wraps it (here, here and here).
All reads in your app go to the local adapter you pick (isolating your app's availability from Cloud) and all writes go to Cloud and then the local adapter.
The result is your feature flag reads are blazing fast. But you get multi-environments, permissions, audit history and more from Cloud.
Automatic Webhooks
The aforementioned configuration would setup polling as the synchronization method with cloud. For production, we don't recommend polling. We want your app to be as isolated from Cloud as possible. It's just good architecture.
Instead, we provide webhooks that ping your app and tell it to sync with us. As of this release, Cloud automatically mounts the webhook middleware in your app if FLIPPER_CLOUD_TOKEN
and FLIPPER_CLOUD_SYNC_SECRET
are configured (#506).
🚨 I'm going to say that again. 🚨
Add two gems.
Add two environment variables.
And you can control your code without changing your code from anywhere.
It's huge folks.
Easy Upgrade
Unfortunately this section isn't automatic. You have to do something. But we made it as easy as we can.
The good news is upgrading the gem should just work™ in a backwards compatible way. 😅
The even better news is if you read over the changelog and follow our upgrade instructions, you'll likely get to remove a bunch of code (a good ol' 🩸 diff).
QA in the Wild
Knowing that this release changes a lot of guts, we battle tested it on Cloud itself, Box Out Sports, and Sailboat Guide prior to releasing to you.
They've all been upgraded for several days without any issues. We hope that our pain is your gain (and trust me there was pain making this smooth for you).
Wrap Up
That's it. We're pumped about this release. It takes simplicity and ease of use to the next level while retaining stability and compatibility.
If you aren't using feature flags yet, you have no reason not to. If you are already using flipper, upgrading should be easy.
Either way, let us know if you have any problems. Happy flipping!
P.S. If you like all of these changes, shout out to @bkeepers on twitter. This was all him. He tried out Cloud a month or so ago, put his pinky to the corner of his mouth and said "This could be simpler."
Making it simpler was a lot of hard work. But now it is.