https://www.gravatar.com/avatar/fd0b8a5e3854d056ee30f07feedf2219?s=240&d=mp

Mario Olivio Flores

Learn how I get things done or check out my resume.

Elixir Tip: Setting Flags with Environment Variables

Background

The FunWithFlags library for Elixir makes it easy to manage feature flags. On a recent project, we needed to set flags in a development environment via environment variables.

Here is an easy way to do that using environment variables.

Create an Initializer

This initializer does a few things:

  1. Loads the flag definitions from config/flags.exs
  2. Loads any env variables that match our flag’s with a predefined pattern
  3. If a matching var is found and corresponds to a boolean flag, it sets the flag to the value of the environment variable.

This is a simple implementation that only allows setting boolean flags.

Here’s an example initializer:

defmodule MyApp.FlagInitializer do
  require Logger

  def init do
    {:ok, all_flags} = FunWithFlags.all_flags()
    Enum.each(all_flags, &update_flag_from_env/1)
  end

  defp update_flag_from_env(%FunWithFlags.Flag{name: flag_name, gates: gates}) do
    if has_boolean_gate?(gates) do
      env_var_name = flag_name
        |> to_string()
        |> String.upcase()
        |> then(&("FEATURE_FLAG_" <> &1))

      case System.get_env(env_var_name) do
        "true" -> update_flag(flag_name, true)
        "false" -> update_flag(flag_name, false)
        nil -> :ok
        _ -> Logger.warning("FEATURE FLAG: #{flag_name} Invalid value set")
      end
    else
      :ok
    end
  end

  defp has_boolean_gate?(gates) do
    Enum.any?(gates, &match?(%FunWithFlags.Gate{type: :boolean}, &1))
  end

  defp update_flag(flag_name, true) do
    {:ok, _} = FunWithFlags.enable(flag_name)
    Logger.info("FEATURE FLAG: #{flag_name} Enabled")
  end

  defp update_flag(flag_name, false) do
    {:ok, _} = FunWithFlags.disable(flag_name)
    Logger.info("FEATURE FLAG: #{flag_name} Disabled")
  end
end

Add to Your Application

You can add this initializer to your application by adding the following to your application.ex file:

#Application
def start(_type, _args) do
  # ...
  # ...
  # ...
  result = Supervisor.start_link(children, opts)

  MyApp.FlagInitializer.init()

  result
end

Of course, there are alternatives to this initializer, but this is a simple approach to set the flags.

Setting Flags

Now, you can set the flag in your environment:

FEATURE_FLAG_MY_FLAG=true

Contents