SimpleFeatureFlags provides basic, simple feature flag functionality. You can use application configuration (config/runtime.exs) to select the deployment environments in which a feature is enabled.

This approach is useful when you want to roll out a feature to only a subset of your environments. Here are a couple of examples:

  1. You want to try a feature in your localhost and staging environments, before rolling it out to production.
  2. You want to use a feature in a subset of your production environments (based on a region, or some other grouping).

The example below describes switching to a new, exciting algorithm (:new_algorithm) for computing 𝝿 in :localhost and :staging deployment environments, before rolling it out to :production (or not).

Installation

The package can be installed by adding simple_feature_flags to your list of dependencies in mix.exs:

def deps do
  [
    {:simple_feature_flags, "~> 0.1"}
  ]
end

The docs can be found at https://hexdocs.pm/simple_feature_flags.

Configuration

Determine the name of the deployment environment you are running in, and list the environments in which your feature is enabled.

In this example, the name of the deployment environment is loaded from an environment variable, and the new_algorithm feature is enabled in localhost and staging:

config/runtime.exs


# Determine the current environment.
# Is the service running on localhost? In the staging environment? In production?
current_deployment_environment =
  System.get_env("DEPLOYMENT_ENVIRONMENT")
  |> case do
    nil -> raise "DEPLOYMENT_ENVIRONMENT is not defined"
    env -> String.to_atom(env)
  end


config :simple_feature_flags, :flags, %{
  # In which deployment environment (e.g., :production, :staging, :localhost, :test) is
  # the code currently running?
  current_deployment_environment: current_deployment_environment,

  # Optional: list possible deployment environments, for additional validation and
  # protection against typos.
  known_deployment_environments: [:test, :staging, :production],

  # In which of the environments do you want to enable each feature?
  features: %{
    new_algorithm: %{enabled_in: [:localhost, :staging]},
    new_ui: %{enabled_in: [:staging]}
  }
}

Using Feature Flags in Your Code

Wrap your feature logic in SimpleFeatureFlags.enabled?/1:

defmodule MyApp.Pi do
  def compute_pi() do
    if SimpleFeatureFlags.enabled?(:new_algorithm) do
      # Use the new, exciting algorithm.
      compute_pi_new_algorithm()
    else
      # Use the old, boring algorithm.
      3.14
    end
  end
end

Optional: Log Configuration on Startup

lib/myapp/application.ex

defmodule MyApp.Application do

  require Logger
  use Application

  @impl true
  def start(_type, _args) do
    Logger.info(SimpleFeatureFlags.current_configuration_to_string())
    ...

Here is an example of the output:

  Current Deployment Environment: :localhost
  Features:
  - new_algorithm is ON. Enabled in [:localhost, :staging].
  - new_ui is OFF. Enabled in [:staging].

Completing the Experiment: Retiring :new_algorithm or Rolling it Out to :production

If, after experiencing :new_algorithm is :localhost and :staging, you decided that the world is not ready for its genius, you can simply remove new_algorithm and its implementation from configs and the codebase.

If you are now confident that this new_algorithm for computing 𝝿 will make the world and your product better, and you want to roll it out to production, you have a couple of options:

  1. Remove the feature flag completely. The new feature is now part of your codebase.
  2. Add your production environment to the enabled_in: list, or replace the list with :all:
    • new_algorithm: %{enabled_in: [:localhost, :staging: :production]}, OR
    • new_algorithm: %{enabled_in: :all},.

A/B, Regional Testing

The example above alluded to the existence of :localhost, :staging, and :production deployment environments.

The configuration approach of choosing the environments in which your changes are live remains the same, regardless of how many production or staging environments you have: simply detect current_deployment_environment in which the code is running, and include all environments of interest in the enabled_in list.

For example, to roll :new_algorithm out to your production environment running in Singapore, but not to the US or Japan, you might do something like this:

config/runtime.exs

# e.g. :localhost, :staging, :production_us, :production_sg, :production_jp
# Detect this in runtime.
current_deployment_environment = ... 

config :simple_feature_flags, :flags, %{
  current_deployment_environment: current_deployment_environment,
  features: %{
    # enabled in production in Singapore.
    new_algorithm: %{enabled_in: [:localhost, :staging, :production_sg]},
  }
}