Blog

Ruby on Rails Apps Using DynamoDB-Based Solution For Feature Flags

Carol Mascarenhas
August 30, 2023

Feature Flags

You’re likely familiar with feature flags—commonly used in software development. They enable us to turn on/off the features in our applications in runtime without requiring a new deployment. So a team can darkly and continuously integrate code into production. The approach offers various scenarios for implementation, benefiting the development processes. For instance:

  • Developers have real-time control over managing and releasing features.
  • They can gradually introduce new features to specific user groups if required.
  • If a new feature causes problems, feature flags enable quick deactivation to prevent disruptions.
  • Various teams can work independently on different parts and quickly address user needs.
  • These flags facilitate seamless A/B testing, enabling informed decisions based on real-world usage.

Using AWS DynamoDB for Feature Flags in Rails Apps

Ruby on Rails developers have different options to address the requirement of implementing feature flags. Consider the simpler approach using YAML or environment variables for straightforward cases without dynamic toggling. Alternatively, you can integrate a specialised gem available in our community. But today, I want to bring your attention to the DynamoDB-based solution—your introduction to a simple and robust feature flag implementation in the cloud as the focus of this blog post.

What is DynamoDB?

DynamoDB is a managed NoSQL database service provided by Amazon Web Services (AWS). Amazon designed DynamoDB to store and retrieve data quickly and at any scale, making it suitable for applications with varying workloads.

How is the Configuration in you Rails App?

Begin by adding the necessary gem to your Gemfile:


gem 'dynamoid'

After installing the library using Bundler, it’s time to create the config/initializers/aws.rb file:


Aws.config.update({
  region: 'ap-southeast-2', # Sydney
  credentials: Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'])
})

Setting Up the Model:

Your model will include information about the DynamoDB table and its attributes. You can refer to the dynamoid gem documentation available at the link: https://github.com/Dynamoid/dynamoid.

Here’s the code for my FeatureFlag model:


class FeatureFlag
  include Dynamoid::Document

  table name: 'feature_flags', key: :id

  field :name, :string
  field :description, :string
  field :enabled, :boolean, default: false

  global_secondary_index hash_key: :name, projected_attributes: :all
end

Managing the Feature Flags

Rake Tasks

The gem provides two helpfull Rake tasks. The first task generates DynamoDB tables for every Dynamoid model you have. Keep in mind, this won’t alter existing tables.


rake dynamoid:create_tables

The second task checks the connection to the DynamoDB instance using your configured settings.


rake dynamoid:ping

CRUD

You will use a similar approach used in active record models to create, retrieve, update and delete your feature flags.


# create
FeatureFlag.create(name: 'habits_feature', description: 'Enable Habits module', enabled: true)

# retrieve
feature = FeatureFlag.find_by_name('habits_feature')

# update
feature = FeatureFlag.find_by_name('habits_feature')
feature.enabled = false
feature.save

# delete
feature = FeatureFlag.find_by_name('habits_feature')
feature.delete

Usage - Views, Helpers and Controllers:

The fine control on your application occurs when your views, helpers, and controllers selectively display features based on the corresponding feature flag.

You can do this using a helper method in your view, for example:


<%= link_to 'Manage Habits', habits_path, class: "dashboard-item" if habits_enabled? %>

The helper needs to check the value of the flag as below:


def habits_enabled?
  FeatureFlag.habits_enabled?
end

Also, in your controllers, you can control the access to routes using before actions. Like the following example:


before_action :habits_enabled

def habits_enabled
  redirect_to root_path unless FeatureFlag.habits_enabled?
end

FeatureFlag.habits_enabled?

As part of this implementation, I’ve introduced two new methods within the FeatureFlag model to handle the logic of retrieving the feature flag status.


METHOD_SUFIX = '_enabled?'
FEATURE_FLAG_SUFIX = '_feature'

def self.method_missing(name, *args)
  if name.to_s.end_with?(METHOD_SUFIX)
    feature_flag_name = name.to_s.gsub(METHOD_SUFIX, FEATURE_FLAG_SUFIX)
    flag = where(name: feature_flag_name)&.first
    flag&.enabled?
  else
    super
  end
end

def self.respond_to_missing?(name, include_private = false)
  name.to_s.end_with?(METHOD_SUFIX) || super
end

This setup allowed me to utilise the method FeatureFlag.habits_enabled? within the helpers and controllers. It also will easily cover the future feature flags I incorporate into the project.

Note that we can always explore the DynamoDB integration in diverse ways to address our app’s unique needs.

Conclusion

I hope you find the implementation presented here helpful. It is a straightforward way to master feature flags using DynamoDB. Explore the full range of possibilities through the Dynamoid gem documentation, and remember that simplicity is the key.

If you have any questions or would like some more information about our Ruby on Rails consulting services, please contact us and we will get back to you promptly!