FeatureFlag
FeatureFlag provides a macro that allows for conditional branching at the function level via configuration values.
In other words, you can change what a function does at runtime by setting/modifying a config value.
Use Case
The goal of this library was to provide an elegant and consistent mechanism for changing what a function does depending on a value that can easily be modified (i.e. a configuration value).
This could very easily be done in plain Elixir via a simple case statement:
defmodule MyApp do
def math(x, y) do
case Application.fetch_env!(:my_app, :math) do
:add -> x + y
:multiply -> x * y
:subtract x - y
end
end
endThere's nothing wrong with this approach, and really no need to reach for anything else.
However, the same code can be rewritten as such using FeatureFlag
defmodule MyApp do
def math(x, y), feature_flag do
:add -> x + y
:multiply -> x * y
:subtract x - y
end
end
When called, each case will attempt to match on the current value of Application.fetch_env!(:feature_flag, :flags)[{MyApp, :math, 2}]).
Beyond removing a marginal amount of code, FeatureFlag provides a consistent interface for defining functions with config-based branching.
Usage
Add FeatureFlag as a dependency in your mix.exs file.
def deps do
[
{:feature_flag, "~> 0.1.3"}
]
end
After that's done, run mix deps.get, and then you can define a feature flag'd function!
Here's a simple example:
defmodule MyApp
use FeatureFlag
def get(key), feature_flag do
:cache ->
get_from_cache(key)
:database ->
get_from_database(key)
end
end
The function MyApp.get/1 will perform different procedures depending on a config value you can set via:
# config/{dev,test,prod}.exs
config FeatureFlag, :flags, %{{MyApp, :get, 1} => :cache}or, you can set/change this value at runtime via:
FeatureFlag.set({MyApp, :get, 1}, :database)If your function is only going to do one of two things based on a boolean feature flag, you can simplify your function like so:
def get(key), feature_flag do
get_from_cache(key)
else
get_from_database(key)
end
The first block will get called if Application.fetch_env!(FeatureFlag, {MyApp, :get, 1}) == true, and the else block will get called if it's false.
Mentions
I'd like to thank the following people who contributed to this project either via code and/or good ideas:
I'd also like to thank @Packlane for giving me time to work on and share this software.