Simple progressive feature rollout

I absolutely love continuous deployment and similarly can’t stand long-running feature branches. The rub is features that actually take a bit of work to get finished up. I’m a big fan of deploying the least possible functionality and iterating from there. Along those lines, I have always liked the idea behind James Golick’s rollout gem. Deploy your code and new features regularly, but decouple deployment and feature availability. And, to take it a step further, don’t limit yourself to deploying a feature all or nothing. Consider release to a select group.

I can’t find the reference, but GitHub uses flags to release features. They have something akin to an “employees” switch that allows them to use features before the rest of us. Brilliant.

While I like rollout, I wasn’t ready to add another gem to our repo. I had a feature I want to rollout in stages, in case anything goes wrong or has unexpected consequences. So I whipped up a simple class using on of my favorite toys, “Rails.cache”.

Now, I can wrap functionality in the view like so:

<% if YourAppName::Feature.active?('billing-report', authenticated_user) %>
  <li><%= link_to 'Billing Report', billing_report_path %></li>
<% end %>

And from the console, I can change the on/off flags (since the feature is off by default in production, and on for other environments) for all members of the User class:

YourAppName::Feature.set_for_user_type('billing-report', User, true)

Or turn it on for a single user:

YourAppName::Feature.set_for_user('billing-report', User.last, true)

Of course, you could get crafty at the console and activate a random group of users, etc.

It’s all dirt simple, but gets the job done. Once the feature is in full usage, I’ll go back and remove the #active? checks.

Here’s the class:

module YourAppName
  # Simple feature on/off switches based on user and a slug. Allows
  # deployment of code that is conditionally used.
  class Feature
    class << self
      # Public: is the feature active for the given user?
      # feature_slug - A String that identifies the feature.
      # user         - A user that is operating the app (an instance
      #                of any ActiveRecord class that identifies who
      #                is authenticated...could be Admin, User, etc.).
      # Returns a Boolean.
      def active?(feature_slug, user)
        # Features are always on when not in production
        return true if !Rails.env.production?

          !!Rails.cache.read(feature_cache_key(feature_slug, user))
        rescue => e
          return false

      # Public: Change the on/off flag for an entire group
      # of users.
      # feature_slug - A String that identifies the feature.
      # clazz        - The ActiveRecord user class to change the flag for.
      # on_off_flag  - Boolean representing whether the feature is on or off.
      # Returns nothing.
      def set_for_user_type(feature_slug, clazz, on_off_flag)
        clazz.all.each do |user|
          set_for_user(feature_slug, user, on_off_flag)

      # Public: Change the on/off flag for a single user.
      # feature_slug - A String that identifies the feature.
      # user         - The user instance to change the flag for.
      # on_off_flag  - Boolean representing whether the feature is on or off.
      # Returns nothing.
      def set_for_user(feature_slug, user, on_off_flag)
        Rails.cache.write(feature_cache_key(feature_slug, user), on_off_flag)


      # Private: generate the slug to be used for identifying the
      # feature within our persistence store.
      # feature_slug - A String that identifies the feature.
      # user         - A user instance (Admin, Buyer, Dealer).
      # Returns a String.
      def feature_cache_key(feature_slug, user)


all posts

26 Simple progressive feature rollout
30 Finding someone else's class names in your app
08 Microgems

07 PPL - Falling off the wagon
07 One domain moved...24 to go
03 PPL: Day 20 - Successive Refinement Part 2
02 PPL: Day 19 - Successive Refinement Part 1
01 PPL: Day 18 - Emergence
30 PPL: Day 17 - Systems Part 2
29 PPL: Day 16 - Systems Part 1
29 PPL: Day 15b - Classes Part 2
28 PPL: Day 15 - FAIL
27 PPL: Day 14 - Classes Part 1
26 PPL: Day 13 - Unit Tests
25 PPL: Day 12 - Boundaries
24 PPL: Day 11 - Error handling
23 PPL: Day 10 - Objects & Data Structures Part 2
22 PPL: Day 9 - Objects and Data Structures Part 1
21 PPL: Day 8 - Horizontal formatting
20 PPL: Day 7 - Vertical formatting
19 PPL: Day 6 - Bad comments
18 PPL: Day 5 - Good comments
17 PPL: Day 4 - Functions continued
16 PPL: Day 3 - Function arguments
15 PPL: Day 2 - Functions
14 PPL: Day 1 - Meaningful names
12 Plan for 30 days of Code Complete
11 Peer Pressure Learning experiment
16 30 technologies in 30 days
18 Determining employee compensation
16 RubyGems error: marshal data too short
08 Take your workspace seriously
04 RVM installation of Ruby Enterprise Edition on Ubuntu Hardy
05 And now for something completely different
21 Validate Ruby on Rails model entries with Cucumber
20 Can you be friends with your clients? I think so.
19 When building web apps, utilize proper indexing
04 Client stats for 2009

31 SOLD: 2.66 Quad Core "Nehalem" Mac Pro
30 SOLD: 13" black MacBook
27 Samantha Joy Harvey joins our family
19 Presenting at the Screaming Monkeys Web Guild meetup
16 Finding my ideal clients
15 Find your ideal clients then take amazing care of them
12 Balancing work and family life when you're the boss
06 Learning from the masters of business and user experience
01 Preview: Rockford Ambulance rebranding
29 rSpec "Can't find first {objectName}"
23 Rev up your Pragmatic Programmer PDF books
20 Screencast: Simple version control with Git
12 Coldfusion 8 displays code and won't execute

02 Making movies with Sony Vegas
21 Finally have Exchange email working on the iPhone

Contact me