Feature Flipping with Rails and Rollout
Sometimes you want the ability to flip a feature on or off, or only give certain users access to new features in your application. Maybe you want to get user feedback before the final release? Maybe you want the ability to revert to the previous functionality with a single command.
With the Rollout Gem, this type of functionality is extremely easy and straight forward to set up in a Rails application.
Rollout allows you to enable or disable features without having to revert changes in the repository or promote different branches to production.
It does this by saving a “feature flag” in Redis which is either on or off. You can enable this feature flag globally, on a per-user basis or toggle the feature on and off for specific groups of users.
In order for Rollout to work, we need to have a Redis client set up in our application. I’ll walk through how I set up Redis for the demo app I created for this write-up. But first, let me show you what the app is.
The demo app is a very simple to-do app. A user can create and delete todos, and mark them as complete.
Setting up Redis
As mentioned above, Rollout uses Redis to store the state of feature flag. So let’s set that up.
Add the redis-rails
and dotenv
gems to you Gemfile
.
gem 'redis', '~> 4.1', '>= 4.1.3' gem 'dotenv-rails', '~> 2.7', '>= 2.7.5', groups: [:development]
Make sure to install these new gems with bundle install
.
Create an env file and add the key for the Redis URL. Here’s what I did for local development.
Create the file.
touch .env.development
Add this to the .env.development
file we just made.
REDIS_URL=redis://localhost:6379
Next, I added an initialization file for Redis
touch config/initializers/redis.rb
and add this to that file.
require "redis" Redis.current = Redis.new(url: ENV['REDIS_URL'])
You can check that this is working by opening up the Rails console and typing Redis.current
. You should see something like this in the console.
=> #<Redis client v4.1.3 for redis://localhost:6379/0>
Right on. Now that Redis is set up, let’s see how you can implement and use Rollout.
Adding Rollout
I’m going to add a feature that allows a user to mark a todo as important. The only thing here is that I’m going to “wrap” this feature markup in Rollout so I can turn it on or off extremely. Let’s set up Rollout first.
Add the rollout gem to your Gemfile
gem 'rollout', '~> 2.4', '>= 2.4.5'
and install with bundle install
.
Now we need to update config/initializers/redis.rb
to get rollout working.
require "redis" Redis.current = Redis.new(url: ENV['REDIS_URL']) # add this line $rollout = Rollout.new(Redis.current)
Make sure you restart your rails server. You can check if this worked by starting up the rails console and running $rollout
.
You should get something back like this.
pre>
Rollout is now ready to use!
Wrapping Features
I’ve added the column important
to the todos
table and updated the view and controller. Here is what the view file looks like.
<% @todos.each do |todo| %> <div class="todo <%= 'important' if $rollout.active?(:important_todos) && todo.important? %> <%= 'complete' if todo.complete? %>"> <div class="todo-text"><%= todo.text %></div> <div class="todo-actions"> <% if $rollout.active? :important_todos %> <span><%= link_to todo.important? ? '!Important' : 'Important', todo_toggle_important_path(todo), method: :put, remote: true, class: 'important' %></span> <% end %> <span><%= link_to todo.complete? ? 'Incomplete' : 'Complete', todo_toggle_complete_url(todo), method: :put, remote: true %></span> <span><%= link_to 'Remove', todo, method: :delete, class: 'remove' %></span> </div> </div> <% end %>
You can see that I’ve wrapped the toggle important link in an if statement.
if $rollout.active? :important_todos
The active?
method checks Redis to see if the `important_todos` feature is…active. If it’s not, this will return false and nothing will be rendered.
Without this feature enabled, the app looks and performs the same as before the new code was added.
If we want to activate this feature, all we have to do is open up the rails console and run
$rollout.activate :important_todos
This will save the important_todos
feature in Redis and set it to active. The next time a user visits the todos page, they will see the link to toggle important todos.
The great thing about this is that everything happens in a live environment. We don’t need to push a different branch if, for whatever reason, this new feature needs to be disabled, or restart any dynos to see the changes take effect.
All we have to do is start up the rails console and run $rollout.deactivate :important_todos
and boom! No more important todos feature.
Here is a short clip of all this in action.
There’s more to Rollout than just flipping a single feature on or off. Like I said before, you can enable features for specific users or place a bunch of users into groups (user testing) and enable features for the whole group. Be sure to check out the Rollout Docs to see how this additional functionality works.