Greycastle Logo

Introducing feature flags to Ruby on Rails

Introducing feature flags to Ruby on Rails

During a meeting a while back I suggested using feature flags to help to release a critical change to our payment system. This came from discussing how we test the change. As we are using Stripe, it is easy enough to test the APIs but we want to test the full flow and see actual money coming into our account. The specific change has to do with how we call the API so it feels better to test it in anger.

Using a feature flag, we could even configure it to only act on a given store so we can test in isolation, in production, before releasing. This means we can deploy from trunk and easily roll back if it blows up. It also gives us the possibility to deploy ahead of time and simply flick the switch.

Homegrown

I began building a very simple feature flagging class to try out the concept:

class Service::FeatureFlags
  def self.is_enabled?(flag)
    flag = flag.to_s.upcase
    env = ENV["FF_#{flag}"]
    is_true?(env)
  end

  private
  def self.is_true?(feature_flag)
    feature_flag.present? && (feature_flag.downcase == 'true' || 
    feature_flag == '1')
  rescue StandardError => e
      Rails.logger.error e
      false
  end
end

Nothing wrong with this as such but it has a few drawbacks:

  1. As we’re using docker-compose locally, we have to run `docker-compose up -d` to make any environment settings update, in our environment this takes a long time
  2. The value is local to the server, since we deploy using kustomize this is fine, beneficial even, however, we can’t flick the switch on a server cluster without restarting them all
  3. It doesn’t have fancy features like switching based on user etc (but, we don’t need that at this point either)

So that prompts the question, are there other options?

Feature toggling alternatives

Turns out there are loads. At featureflags.io we can read not only what feature flags are but also find a long list of Ruby specific SDKs. If you want to know what feature flags are though, I recommend reading Martin Fowler’s post on Feature Toggles which is very comprehensive.

LaunchDarkly

LaunchDarkly stands out since I keep getting their newsletter for some reason. They provide feature toggling as a service. This is really nice, out of the box, packaged, maintained and ready to go. It would mean we can easily scale our feature flags across not only instances of our service, but across different services and even languages as they provide several different SDKs. Very yummy.

However, it also means another 3rd party service to integrate with, across several environments and likely, down the line, incurring cost. Smells like premature complexity to me. However, I will make a mental note of it for the future.

Rollout

Rollout looks very simple and relies on Redis. This has a few benefits such as toggling once and hitting all servers. We can also easily control the values using the rails console. This means we have to actually ssh into the running server instances to flick switches though, useful but I’d rather not touch the servers directly if avoidable.

Mr Leonard has a good blog entry on how to use the rollout gem.

Flip

The flip gem has everything you need, environment flipping, dashboard and caching. The drawback is that it’ll use your database so you’ll end up doing a migration to add this and also, of course, keep another table in there. Now, since almost all Rails instances have a database this might be ideal. The dashboard is possible to secure as you like as well, keeping curious eyes away.

I’m pretty happy with the features of this library though I feel, as with LaunchDarkly, that it might be more complicated than what we need at this time. Also, since it’s tightly coupled the database I would think twice about using these flags for anything else than this one application. This means we’ll end up having another flagging system elsewhere if we add microservices.

Verdict

I like the simplicity of Rollout. The dashboard of flip and the breadth of functionality of LaunchDarkly. But in the end, using environment variables is a simple solution. It is enough for what I want to do without adding any extra complexity.

Result

After putting the small class above to use, I deployed and tested. It’s important to check the functionality both with the flag set and unset. Especially in my case where I had to change existing code.

# Changing existing
def Class
  def my_func
    if FeatureFlags.is_true?(:new_func)
      # do new code updatetd code
    else
      # do old code
    end
  end
end

An alternative would be to wrap the old and new functionality in a class which has the same signature and simply switch there. This way we don’t need to change the existing functionality at all, only refactor.

# Extension by using decorator
def DecoratorClass
  def initialize
    @old = OldClassName.new
    @new = NewClassName.new
  end

  # still same contract of method
  def my_func
    use_class = FeatureFlags.is_true?(:new_func) ? @new : @old
    use_class.my_func
  end
end

When this was merged to master and deployed to production, we could test it live. For just a minute during the test we activated the flag. We use kubernetes so it was simple to do this by editing the environment variables in the deployment:

kubectl edit deplyoment my-deployment

Having both old and new functionality in the code simultaneously does require some more testing. However, it is perfect for separating when we do stuff from when we deliver it.

This way, I have now completed and got sign off on my task, a month ahead of the release date. The only thing remaining for release is switching the default value in the config. Awesome.

Are you using feature flags? How? Does it help, what’s the drawbacks? Share your experience in the comments!

© 2024 Greycastle