DEV Community

Cover image for Add Feature Flags in Ruby on Rails with Flipper
Hans Schnedlitz for AppSignal

Posted on • Originally published at blog.appsignal.com

Add Feature Flags in Ruby on Rails with Flipper

Picture this scenario: you are a Rails developer and have spent the last couple of days developing that awesome feature that everyone is waiting for. It's big and complex, but it went through rigorous testing, so you are confident everything works as it should. There are deadlines to meet, so you deploy. Immediately, all hell breaks loose.

Your feature straight up breaks the entire app for some of your users. It's hard to say why. No bugs showed up during testing. You revert your change, but the damage has been done. Your customers aren't happy, and you will spend the foreseeable future doing damage control and investigating what went wrong.

The problem is that releasing complex changes is inherently risky, no matter how much testing you do. It would have been nice if you had rolled this feature out gradually, maybe only to a handful of users first. It would have been even better if you could have switched the feature off as soon as the first problem showed up.

This is precisely where feature flags come in. In this post, we'll dive into the ins and outs of feature flags in Rails, including how to set them up using the Flipper gem. But first: what are they?

What Are Feature Flags in Ruby on Rails?

Feature flags are a way to enable or disable a feature in your application while it is running. If a flag is enabled, the feature is used. Otherwise, it is not. Imagine something like this in pseudocode.

if feature_enabled?(:my_awesome_feature)
  # Run awesome feature code
else
  # Not so awesome code
end
Enter fullscreen mode Exit fullscreen mode

The name 'feature flag' is somewhat misleading. Controlling application behavior at runtime is not limited to features,
but applies to any function within your program. You can use feature flags to try out performance tweaks and run A/B tests, for example.

A significant difference between a feature flag and any other old conditional is you can modify feature flags at runtime.

For illustration purposes, let's imagine a simple feature flagging mechanism using environment variables.

def feature_enabled(feature_name)
  ENV[feature_name.to_s.upcase]
end
Enter fullscreen mode Exit fullscreen mode

By modifying the value of the environment variable, we can switch parts of our application on or off at will. Which part of an application? Anything you want!

You could hide parts of the UI if a feature is disabled. You could switch out parts of your code, which is very useful if you aren't confident in your latest refactor. One of my favorite uses of feature flags is to hide entire routes using routing constraints:

# lib/constraint/feature_constraint.rb
class FeatureConstraint
  def initialize(feature)
    @feature = feature
  end

  def matches?(_request)
    feature_enabled?(@feature)
  end
end
Enter fullscreen mode Exit fullscreen mode
# config/routes.rb
Rails.application.routes.draw do
  get 'statistics', to: 'statistics#index', constraints: FeatureConstraint.new(:statistics)
end
Enter fullscreen mode Exit fullscreen mode

We can already see that there are lots of applications for feature flags. Our naive implementation has one significant weakness, though. A feature is either on or off for every user.

What if we want the ability to enable features only for specific users or groups of users, or only for some of the time? Let's take a look at Flipper.

Using the Flipper Gem for Feature Flags

Flipper is a gem that makes feature flags and different ways to toggle them
available in Rails. It is highly modular. Apart from the main gem, you'll also have to pick a storage adapter — but more on that later. Let's use the ActiveRecord adapter for now.

Add the following lines to your Gemfile and run bundle install:

gem 'flipper'
gem 'flipper-active_record'
Enter fullscreen mode Exit fullscreen mode

When we use the ActiveRecord adapter, Flipper will store your feature flag in the database and thus requires setting up new database tables. Flipper comes with a handy generator that will create the necessary migrations.

rails g flipper:active_record
rails db:migrate
Enter fullscreen mode Exit fullscreen mode

You can use Flipper in the same way as the naive implementation we used previously.

if Flipper.enabled?(:my_awesome_feature)
  # Run awesome feature code but with Flipper!
else
  # Not so awesome code
end
Enter fullscreen mode Exit fullscreen mode

The big difference is that instead of modifying environment variables, you now get to toggle features using Flipper's user interface. Add the Flipper UI gem and install it.

gem 'flipper-ui'
Enter fullscreen mode Exit fullscreen mode

Modify your routes file to mount Flipper UI.

Rails.application.routes.draw do
  mount Flipper::UI.app(Flipper) => '/flipper'
end
Enter fullscreen mode Exit fullscreen mode

If you start your application and navigate to the Flipper UI, you will see an overview of all available features and their status. Go ahead and create a my_awesome_feature feature.

The Flipper UI overview

Feature Toggles in Flipper

You've probably noticed that you can do much more than simply enable or disable a feature in Flipper UI. Flipper allows us to enable features for a subset of users or even for a percentage of the time. You can configure your feature flags using one of five different mechanisms.

One of them is the boolean toggle, a global on-off switch. The other four are a lot more interesting than that: groups, actors, percentage of actors, and percentage of time.

Groups

To enable a feature only for a particular group of users, use the group toggle. If it doesn't exist, create the file config/initializers/flipper.rb and add the following code to register a new group — for example, paid users.

# config/initializers/flipper.rb
Flipper.register(:paid_users) do |actor, _context|
  actor.respond_to?(:paid_users?) && actor.paid?
end
Enter fullscreen mode Exit fullscreen mode

To use this new toggle, modify the Flipper invocation to pass the user — or actor, as the documentation likes to call them — and the feature name.

if Flipper.enabled?(:my_awesome_feature, user)
  # Run awesome feature only if the user is paid
else
  # Not so awesome code
end
Enter fullscreen mode Exit fullscreen mode

Flipper will use the block you defined in the initializer to decide if the feature is enabled for this user. In this case, it will invoke the paid? method. You can now use this group in Flipper UI. Click the 'Add a Group' button and select your group.

Adding a Flipper UI group feature

Your group doesn't have to be paid users. It could be members of your team or users who have signed up for a beta program. Using the group toggle is helpful if you want to release something to a subset of users. Imagine beta releases or internal releases.

Actors

Sometimes, you might want to release something to a single individual. In this case, the actor toggle is something to consider.

You don't have to change anything in your initializer to use this mechanism. However, you'll need to ensure that your actor model — which will be your user model most of the time — implements flipper_id. Including Flipper::Identifier takes care of that.

# models/user.rb
class User < ApplicationModel
  include Flipper::Identifier
end
Enter fullscreen mode Exit fullscreen mode
User.create.flipper_id # User;1
Enter fullscreen mode Exit fullscreen mode

You can then use this ID to enable a feature for individual users.

Adding a Flipper UI actor feature

This feature toggle is handy if you want to preview functionality for particular individuals. Think about enabling specific test accounts for partners who need access to a feature before it's released to a broader audience.

Percentage of Actors

If you want to ramp up feature usage slowly, the percentage of actors toggle is for you. Imagine you've implemented some performance optimization, and you're not quite sure how it will behave in production. You would roll this out to a small percentage of users first and then slowly ramp up the usage while monitoring your application.

Adding a Flipper UI percentage of actors feature

If things go sideways, you can always reset the percentage to zero.

Percentage of Time

Last but not least, you may enable a feature for a percentage of requests. You do not need to provide an actor when using this toggle.

if Flipper.enabled?(:my_awesome_feature)
  # Run awesome feature code only for a percentage of requests
else
  # Not so awesome code
end
Enter fullscreen mode Exit fullscreen mode

I like to use this to compare the performance of two different implementations. Note that this feature toggle is random.
The same user will experience different behavior on each request.

Advanced Configuration in Flipper

We've already seen that we can use the ActiveRecord adapter to store feature flag information in our database. But depending on your specific needs, you might want to use a different adapter.

For the most part, all you need to do when swapping out storage adapters is to install the respective gem and configure Flipper accordingly. For example, if you want to use the Redis adapter, you add the flipper-redis gem to your
Gemfile and update the Flipper initializer.

gem 'flipper-redis'
Enter fullscreen mode Exit fullscreen mode
# config/initializers/flipper.rb
Flipper.configure do |config|
  config.adapter { Flipper::Adapters::Redis.new(Redis.new) }
end
Enter fullscreen mode Exit fullscreen mode

Apart from configuring storage adapters, there are a lot of other aspects of Flipper that you can tweak.

Adding feature flags incurs a slight performance penalty, so you usually want to add memoization and caching. You'll likely also want to restrict access to the Flipper UI to authorized users. These topics are out of scope for this post, but I recommend you check out the Flipper documentation if you want to dive deeper.

Caveats to Feature Flags in Rails

Feature flags are great. But as with so many things, there are tradeoffs to consider.

Adding feature flags and maintaining them adds organizational overhead. Releasing and rolling out hidden features using flags is not as simple as deploying 'regular' code — you'll have to remember to turn it on, of course! That usually isn't too hard, but cleaning up feature flags that you don't need anymore might be.

Every time you add a feature flag to your code, you add a conditional, and if you don't watch out, your code quickly becomes littered with them. Keeping track of when and why you created certain feature flags could be challenging. Sadly, Flipper UI itself does not offer much to manage feature flags.

You should also be aware that using feature flags will incur a minor performance penalty. There are ways to mitigate this, but it can have a noticeable impact if you misconfigure or misuse Flipper.

Wrap Up: Get Started with Feature Flags in Ruby on Rails

We looked at how feature flags can help you release with more confidence, learned how they function in principle, and saw how you can get started with feature flags using the Flipper gem.

There are many situations where feature flags are handy. Depending on your use case, you might reach for one of the five different toggles — boolean, group, actor, percentage of actors, or percentage of requests.

Although working with feature flags has its caveats and might take some getting used to, they are nevertheless a great tool to have at your disposal.

Until next time, happy coding!

P.S. If you'd like to read Ruby Magic posts as soon as they get off the press, subscribe to our Ruby Magic newsletter and never miss a single post!

Top comments (0)