Introducing rollout: Condionally roll out features with redis


Aug 01, 2010

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.