Introducing rollout: Condionally roll out features with redis
When we work on new features, we like to push them to production regularly. We've found that long-lived branches tend to introduce more defects than short-lived ones. And as useful as staging can be, it's hard to beat seeing and tweaking new features on the real, production site and infrastructure.
When we're ready to alpha the feature, we'll roll it out to staff. For beta, we might roll it out to some specific friends or people who request access. Then, when it's time to go live, we'll roll it out to a percentage of people at a time to make sure that any remaining performance issues are caught without bringing down the entire application.
If we do find a problem, we need to be able to disable the feature in real-time.
We do all of this using a tool we put together called rollout. It allows us to roll out features to specific users, to pre-defined groups, to a percentage of users, or to any number of combinations of those options. It uses redis to store all of the configuration, so we can easily manipulate rollouts in real-time.
How it works
gem install rollout
I like to assign an instance of Rollout to a global variable.
$redis = Redis.new $rollout = Rollout.new($redis)
I can check whether a user has access to a feature like this:
$rollout.active?(:chat, User.first) # => true/false
Let's say I want to roll out a chat feature. I'd wrap any chat-related code in:
if $rollout.active?(:chat, @current_user) # chat-related code end
The simplest way to start rolling out our chat feature is by giving access to a single user:
$rollout.activate_user(:chat, User.find_by_nickname("jamesgolick")) $rollout.active?(:chat, User.find_by_nickname("jamesgolick")) # => true
When alpha testing, it's convenient to be able to provide access to whole groups of users (staff, for example) at once. We define several groups when we initialize Rollout.
$rollout.define_group(:caretakers) do |user| user.caretaker? end
To provide access to a group:
$rollout.activate_group(:chat, :caretakers)
When it's time to go live, we can slowly ramp up access:
$rollout.activate_perecentage(:chat, 10)
Performance issue? Bug? Remove everybody's access while you retool:
$rollout.deactivate_all(:chat)
More fine-grained deactivation controls exist. See the README for more details.
Get it!
gem install rollout
The code is on github.