FeatureFlipper
A simple, easy-to-integrate feature flag system for Elixir applications with Consul integration, caching, and deadline management.
Features
- Simple Definition: Define feature flippers as simple lists
- Automatic Discovery: Automatically discovers feature flippers from your application
- Consul Integration: Seamless integration with Consul KV store
- Caching Layer: High-performance in-memory caching with
:persistent_term - Environment-Aware Logic: Different behavior for production vs non-production environments
- Configuration Management: Runtime configuration reloading
- Deadline Management: Automatic deadline checking and warnings
Roadmap:
- Better Test support for parent projects
- Best test coverage for the library
- Configurable Timeout for Consul requests
- Support consul watch for picking up automatic changes from consul
Installation
Add feature_flipper to your list of dependencies in mix.exs:
def deps do
[
{:feature_flipper, "~> 0.1.0"}
]
endConfiguration
Add configuration to your config/config.exs:
config :feature_flipper,
app_name: "MyApp", # Required: Your application name for feature flipper discovery
consul_kv_path: "telephony/services/routing",
consul_client_http_addr: "http://consul.query.dev.telnyx.io:18500",
env: System.get_env("ENV", "dev"),
hostname: System.get_env("SERVER_HOSTNAME", "local"),
version_suffix: "1.7.9-rc" # Optional, for production versioningUsage
1. Define Feature Flippers
Create a module in your application to define feature flippers:
# lib/my_app/feature_flippers.ex
defmodule MyApp.FeatureFlippers do
def feature_flippers do
[
{:use_warehouse_cache_tbs_2221?, %{
deadline: ~D[2025-06-30],
description: "Use warehouse cache for improved performance"
}},
{:enable_warehouse_cache_mirroring_tbs_2395?, %{
deadline: ~D[2025-06-30],
force_disable: false,
description: "Enable cache mirroring for data consistency"
}},
{:user_rate_tariff_manager_tbs_2873?, %{
deadline: ~D[2025-06-30],
key: "custom_tariff_key", # Optional custom Consul key
description: "Use new tariff manager for user rates"
}}
]
end
end2. Initialize the System
Add initialization to your application's start phase:
# lib/my_app/application.ex
defmodule MyApp.Application do
use Application
def start(_type, _args) do
children = [
# ... other children
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
end
def start_phase(:init_feature_flippers, _start_type, _phase_args) do
FeatureFlipper.ConfigLoader.init()
:ok
end
end3. Use Feature Flippers
Check feature flipper values in your application:
# Simple check
if FeatureFlipper.enabled?(:use_warehouse_cache_tbs_2221?) do
# Use warehouse cache
use_warehouse_cache()
else
# Use regular cache
use_regular_cache()
end
# Get all feature flags
all_flags = FeatureFlipper.all_flags()
# Returns: %{use_warehouse_cache_tbs_2221?: true, enable_warehouse_cache_mirroring_tbs_2395?: false}
# Reload configuration
case FeatureFlipper.reload() do
:ok -> Logger.info("Configuration reloaded successfully")
{:error, _reason} -> Logger.error("Failed to reload configuration")
endEnvironment Behavior
Non-production environments (dev, test, staging)
-
Features are enabled by default unless
force_disable: trueis set - Consul is not required to be available
- Graceful fallback to default values
Production environment
- Features are controlled by Consul KV data
- Consul connectivity is required for accurate feature flag values
- Supports hostname-specific feature flags
Consul Integration
Path Structure
The library automatically builds Consul paths based on your configuration:
- Production:
{consul_kv_path}/feature_flippers[_{version_suffix}] - Non-production:
{consul_kv_path}/feature-flippers[_{version_suffix}]
Hostname Targeting
You can target specific hostnames by appending the hostname to the key:
# General key
my_feature_flag: true
# Hostname-specific key (takes precedence)
my_feature_flag_server1: falseData Format
Consul values can be stored as:
-
JSON booleans:
true,false -
String booleans:
"true","false"
API Reference
Main Interface
# Check if a feature is enabled
FeatureFlipper.enabled?(:my_feature)
FeatureFlipper.enabled?("my_feature")
# Reload configuration from Consul
FeatureFlipper.reload_configuration()
# Get all feature flags and their values
FeatureFlipper.all_flags()Definition Options
When defining feature flippers, you can specify:
:deadline- Date when the feature should be removed (Date struct):description- Human-readable description of the feature:force_disable- Force disable in non-production environments (boolean, default: false):key- Custom Consul key (string, optional - defaults to the flipper name)
Deadline Management
TODO: This is not yet properly implemented
The library automatically tracks feature flipper deadlines:
# Check for overdue features
warnings = FeatureFlipper.check_deadlines()
# Returns: ["Feature 'old_feature' is past its deadline of 2024-01-01"]
# This can be integrated into your monitoring/alerting systemError Handling
The library provides comprehensive error handling:
- fail fast: fail fast if consul is not available
- Invalid data: Graceful parsing with sensible defaults
- Configuration errors: Clear error messages and logging
Performance
- In-memory caching: Fast access to feature flag values
- Minimal overhead: Efficient caching layer with
:persistent_term - Batch loading: Loads all configuration at startup
Testing
The library includes comprehensive test coverage and provides mocking capabilities for testing your feature-flagged code.
Contributing
- Fork the repository
-
Create your feature branch (
git checkout -b feature/amazing-feature) -
Commit your changes (
git commit -m 'Add amazing feature') -
Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.