README.md

# 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`:

```elixir
def deps do
  [
    {:feature_flipper, "~> 0.1.0"}
  ]
end
```

## Configuration

Add configuration to your `config/config.exs`:

```elixir
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 versioning
```

## Usage

### 1. Define Feature Flippers

Create a module in your application to define feature flippers:

```elixir
# 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
end
```

### 2. Initialize the System

Add initialization to your application's start phase:

```elixir
# 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
end
```

### 3. Use Feature Flippers

Check feature flipper values in your application:

```elixir
# 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")
end
```

## Environment Behavior

### Non-production environments (dev, test, staging)
- Features are **enabled by default** unless `force_disable: true` is 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: false
```

### Data Format

Consul values can be stored as:
- JSON booleans: `true`, `false`
- String booleans: `"true"`, `"false"`

## API Reference

### Main Interface

```elixir
# 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:

```elixir
# 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 system
```

## Error 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

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## License

This project is licensed under the MIT License - see the LICENSE file for details.