Flippin' and Rollin'

Two months ago I wrote up a doozy of a post about flippin’ feature at runtime. Since then, the contributions to Flipper from myself and the community have continued. Easy migration from Rollout, bug fixes for several adapters (ActiveRecord, Sequel and RedisCache) and new UI configuration top the list from the 0.12.x changelog.

Migrating from Rollout

Based solely on GitHub stargazers (at the time of this writing), Rollout is the most popular Ruby feature flipping library. Granted Rollout has a few more years of life than Flipper, but stars are stars. Having a successful project is all about reducing friction. In that vein, this version of Flipper makes it easy to migrate from Rollout to Flipper.

Here is an example of migrating from rollout to the Flipper redis adapter. Feel free to substitute any other adapter for the redis one.

require 'redis'
require 'rollout'
require 'flipper'
require 'flipper/adapters/redis'
require 'flipper/adapters/rollout'

redis = Redis.new
rollout = Rollout.new(redis)

rollout_flipper = Flipper.new(Flipper::Adapters::Rollout.new(rollout))
redis_flipper = Flipper.new(Flipper::Adapters::Redis.new(redis))

redis_flipper.import(rollout_flipper)

Require the files, setup the flipper instances and use import (added in 0.11) to import your rollout features along with their enabled users and groups (see caveats in the  README). You can thank @alexwheeler for the rollout adapter and docs.

Bug Fixes

I never have bugs in my software, but if I were to they might look like the hypothetical ones listed below.

:bug: There was a sneaky one in the ActiveRecord adapter that caused disabled features to be excluded from preloading. Preloading used an INNER JOIN to get all features and gates, but because Flipper clears all gates for disabled features, they had no rows in the gates table which meant they were excluded.  @geetotes reported this in  #324 and I swapped out the INNER JOIN for a LEFT JOIN to fix the problem.

:bug: As reported by @mildmojo in #296 (comment), the ActiveRecord adapter’s unique index on the gates table meant that enabling the same actor or group more than once popped an ActiveRecord::RecordNotUnique exception. Because the Sequel adapter is nearly identical to the ActiveRecord one, it suffered the same problem (popping Sequel::UniqueConstraintViolation). I fixed both in #313 by sprinkling some rescue’s in.

:bug: @kidsalsa pointed out in #323 (comment) that the RedisCache adapter was using an incorrect key in one spot, which was causing cache misses thus hurting hit rates. #325 fixed the issue in a few lines of code and added a regression test to make sure I do not break it again.

Additions/Changes

Flipper::UI

:heavy_plus_sign: #306 is the first step toward making the Flipper UI more configurable. It made the title and description for each of the gates customizable.

For example, customizing the gates like this:

Flipper::UI.configure do |c|
  c.percentage_of_actors.title = "% of Actors"
  c.percentage_of_actors.description = "percentage of actors rule :)"

  c.actors.description = "Enable any actor by entering that actor's flipper_id"

  c.delete.title = "DO NOT PRESS!"
  c.delete.description = "Who knows what will happen?!"
end

Would result in the UI looking like this:

Note the text changes in the image above. Thanks goes to @alexwheeler for the nice Flipper::UI.configure stuff and the ability to configure gate titles and descriptions.

:heavy_plus_sign: Along the same lines, #322 made it possible to disable feature removal. This is not apart of Flipper::UI.configure yet, but will be moved in soon. Thanks goes to @mateusg.

:heavy_plus_sign: Thanks to @jakubkosinski, the redis dependency was relaxed in #317 to include Redis 4.0.x.

:heavy_plus_sign: A common thing to do is to change Flipper to use the memory adapter for tests. Doing so keeps tests snappy and clean between tests. In #309, I added a Flipper.instance= writer method to make it easy to override the default Flipper instance in tests.

# in setup, before, or whatever your test framework calls it
Flipper.instance = Flipper.new(Flipper::Adapters::Memory.new)

Note: This only works if you are using the default Flipper instance configuration (more on that here).

Next Up

Most of my current work has been spent on Flipper Cloud. Over the holidays I started on client side instrumentation for tracking Feature#enabled? events and sending them to Flipper Cloud for processing. This will enable (haha, see what I did there) us to provide you with dope analytics (and beautiful graphs of course) on what your users are doing, simply as a side effect of feature flags.

Imagine a world where you front each feature in your application with a Flipper feature and receive an abundant amount of insight as to which features your users are using and if they are succeeding or not. Sounds pretty great right? I know I am stoked.

Update: Flipper Cloud is now released.