
Privé : Feature flags at SquareScale
Feature flags at SquareScale
We started to talk about feature flags a while ago at SquareScale but never had the time to introduce themin our web application. None of us had the chance to work with feature flags in a previous application so we did not know very much about how to implement them, how to manage them in the application lifetime, and the most important part is that we didn’t have any feedbacks about feature flags being used in production. That being said, we of course read about it. We especially read Martin Fowler’s article: Feature Toggles (aka Feature Flags). There are many reasons we need feature flags.
First, and as I said in my previous blog post, we give a lot of importance to continuous delivery. Only code pushed and used on our production platform has value. But sometimes you don’t want to enable it for everybody.
Second, new features can break things. What about having a feature flag for new features? It would allow us to disable them without having to rollback our docker image, database, and such, in production. Moreover, it would be instant.
A couple of weeks ago, we decided to introduce them into our ruby on rails backend application. We had a look at what existed, and two gems caught our attention.
Rollout: It is the highest ranking gem to deal with feature toggles, however, it relies on Redis,
but it gives us a lot of interesting features such is incremental rollout, specific user enabling etc.
Flipper: It has various adapters, and doesn’t rely specifically on Redis. It’s simple to use and seems to do what we need it to. We decided to go with Flipper. Mainly because it had an active record adapter, and a UI to manage the feature flags.
I will not dig into what Flipper offers because you can read everything on their Github (or because RTFM!). As a very quick sumary you can name flags and then choose several options to enable them. You can enable them for users, group of users, choose a percentage of actors, or a percentage of time. In our case, the group of users feature interested us the most, as we will mainly use feature flags to test new features first.
Flipper set up
The setup is really easy. We just needed to add three gems. One is for flipper, two is for the UI, three is for the active record adapter.
gem 'flipper'
gem 'flipper-ui'
gem 'flipper-active_record'
A rails command allows us to generate the migrations, creating two tables.
rails g flipper:active_record
A bit of configuration into config/initializers/flipper.rb
.
require 'flipper/adapters/active_record'
Flipper.configure do |config|
config.default do
adapter = Flipper::Adapters::ActiveRecord.new
Flipper.new(adapter)
end
end
Adding the route for the UI.
mount Flipper::UI.app(Flipper) => '/flipper'
And that’s it. You’re all done!
Using Flipper
As I said, we wanted to use Flipper to allow access to our new features to a group of users. The documentation shows how to do it:
# this registers a group
Flipper.register(:admins) do |actor|
actor.respond_to?(:admin?) && actor.admin?
end
Here, an actor is anything having a flipper_id
method.
In our case our actors will be instances of User
.
We simply had to add
Contract None => String
def flipper_id
"User;#{id}"
end
(I know we use contracts in Ruby, stay tuned for more information)
The thing in that example is that you have to introduce a boolean into your actor class for every group you want to create.
We don’t want to pollute our user class with a lot of booleans. We chose to create a FlipperMembership
class containing all those booleans for a User
with a :has_one
relation.
class CreateFlipperMembership < ActiveRecord::Migration[5.0]
def change
create_table :flipper_memberships do |t|
t.boolean :new_feature_1_tester, default: false
t.belongs_to :user, index: true
end
User.all.each do |user|
user.update flipper_membership: FlipperMembership.new
end
end
end
class User < ApplicationRecord
has_one :flipper_membership
end
And then to add a is?
method on the User
class (thanks for the idea).
Contract Symbol => Bool
def is?(group)
flipper_membership.send(group)
end
This way we can declare any Flipper group without forgetting to update our FlipperMembership
model.
Flipper.register(:db_choices_testers) do |actor|
actor.respond_to?(:is?) && actor.is?(:new_feature_1_tester)
end
Now we just have to protect our code with Flipper:
if Flipper.enabled? :new_feature_1, current_user
...
end
And that’s all for now folks. It is really new for us at the moment, that’s why we have no feedback to give yet (except it is really easy to set up). We will certainly write an other blog post about that in the coming months, stay tuned!
Cheers,
Special thanks to Haze for his feedback.
