Feature Flagging Asynchronous Workers
Tags: ruby, rails
Published: 2020-07-27 22:45:12

In the project we are working on, we heavily use Feature Flags to gradually roll new functionality. Also, it let us turn them off in case something is causing problems.

Since all of our business code gets executed asynchronously, I recently found myself repeating this chunk of code in my workers.

```
class BaseWorker
  include Sidekiq::Worker
end

class SendNotificationEmailWorker < BaseWorker
  def perform
    return unless FeatureFlags.enabled?(:send_emails)

    # do work
  end
end
```

This code is simple and it works, but what if I don't want to repeat the feature flag check in every worker I define?

Well, as always, we can leverage some Ruby concepts to encapsulate this behaviour and avoid repeating ourselves.
First of all, let's define how we want this to work:

```
class SendNotificationEmailWorker < BaseWorker
  feature_flagged(:send_emails)
  
  def perform
    # do work
  end
end
```

As you see, I want to call a class method `feature_flagged` that receives a flag key and that it avoids calling `SendNotificationEmailWorker#perform` in case it's turned off.

Before showing you the code, should understand the following concepts:
1. How the Ruby Ancestor chain works
2. Difference between `include` and `prepend`
3. The `super` keyword
4. Anonymous modules
5. Class variables

```
class BaseWorker
  include Sidekiq::Worker

  class << self
    def feature_flagged(feature_flag)
      class_attribute :feature_flag
      self.feature_flag = feature_flag

      anonymous_module = Module.new do
        def perform(*args)
          unless ::FeatureFlags.enabled?(feature_flag)
            logger.info("Skipping execution since #{feature_flag} is turned off")
            return
          end

          super
        end
      end

      prepend anonymous_module
    end
  end
end

class SendNotificationEmailWorker < BaseWorker
  feature_flagged(:send_emails)

  def perform
    # work
  end
end
```

As you see:
1. I created an Anonymous Module that defines the `#perform` method.
2. The Anonymous Module is prepended to `SendNotificationEmailWorker` so it's invoked *before* `SendNotificationEmailWorker#perform`. By doing this, we can control if `SendNotificationEmailWorker#perform` should be invoked depending on the feature toggle.
3. If it's on, we call `SendNotificationEmailWorker#perform` by doing `super`. Calling `super` without arguments guarantees that all arguments are passed by to the next implementation.
4. In order for the module to be able to access the `feature_flag` value, we needed to define it somehow. Since I am in a Rails app I used [class_attribute](https://apidock.com/rails/Class/class_attribute)

> `Declares an attribute whose value is inheritable by subclasses. Subclasses can change their own value and it will not impact parent class.`

Now, let's analyze the pros and cons of this code.

*Pros*
1. DRY. No more repeating in all your workers' perform method `return unless FeatureFlags.enabled?(feature_flag)`.
2. When looking at a class definition, you will identify quicker if it depends on a feature flag or not. For example:

```
class SendNotificationEmailWorker < BaseWorker
  feature_flagged(:send_emails)

  def perform
    # send_email
  end
end
```

versus

```
class SendNotificationEmailWorker < BaseWorker
  def perform
    return unless FeatureFlags.enabled?(:send_emails)
  end
end
```

*Cons*
1. Anonymous Modules are less performant than non-anonymous ones. If you don't abuse from the feature you shouldn't really worry about it but it's something to consider when introducing them.
2. This doesn't stop your worker from getting queued and executed. If you are concerned about performance or consuming your queue storage when you know that you won't execute the code maybe you should focus on trying to not queue it at all.
3. As mentioned in the `class_attribute` definition, once the `feature_flagged` method is called, a class variable `feature_flag` value will be defined. This means that all the subclasses that inherit from the class that called this method will have that value although it can be overridden. In our case this is fine.