# Feature Flags System Guide

## Overview

The Feature Flags System provides a lightweight, flexible way to control feature rollout, perform A/B testing, and manage gradual deployments in your Python applications. It supports multiple configuration sources, percentage-based rollouts, and runtime flag management.

## Table of Contents

1. [Core Concepts](#core-concepts)
2. [Quick Start](#quick-start)
3. [Configuration](#configuration)
4. [Usage Patterns](#usage-patterns)
5. [Advanced Features](#advanced-features)
6. [Best Practices](#best-practices)
7. [CLI Commands](#cli-commands)
8. [Testing with Feature Flags](#testing-with-feature-flags)

## Core Concepts

### Feature Flag States

Feature flags can be in one of four states:

- **enabled**: Feature is active for all users
- **disabled**: Feature is inactive for all users
- **percentage**: Feature is active for a percentage of users (0-100)
- **conditional**: Feature state depends on context (user attributes, environment, etc.)

### Configuration Priority

The system checks configuration sources in this order:
1. Runtime overrides (set programmatically)
2. Environment variables
3. JSON configuration files
4. Default values

## Quick Start

### Basic Usage

```python
from augint_library.feature_flags import feature_flag, FeatureFlags

# Initialize the feature flags manager
flags = FeatureFlags()

# Register a feature flag with a default state
flags.register("new_dashboard", default="disabled")

# Simple feature check
if flags.is_enabled("new_dashboard"):
    show_new_dashboard()
else:
    show_legacy_dashboard()
```

### Using the Decorator

```python
from augint_library.feature_flags import feature_flag

@feature_flag("experimental_api", fallback=legacy_api_response)
def new_api_endpoint():
    """This function only runs if 'experimental_api' flag is enabled."""
    return {"status": "success", "version": "2.0"}

def legacy_api_response():
    """Fallback function when flag is disabled."""
    return {"status": "success", "version": "1.0"}
```

### Percentage-Based Rollout

```python
# Enable for 25% of users
flags.register("new_algorithm", default="percentage:25")

# Check with user context for consistent hashing
user_id = "user123"
if flags.is_enabled("new_algorithm", context={"user_id": user_id}):
    # User will consistently see the same state
    use_new_algorithm()
```

## Configuration

### Environment Variables

Set feature flags via environment variables:

```bash
# Enable a feature
export FEATURE_FLAG_NEW_DASHBOARD=enabled

# Disable a feature
export FEATURE_FLAG_EXPERIMENTAL_API=disabled

# Percentage rollout
export FEATURE_FLAG_NEW_ALGORITHM=percentage:50

# Conditional (requires custom evaluator)
export FEATURE_FLAG_PREMIUM_FEATURES=conditional
```

### JSON Configuration

Create a `feature_flags.json` file:

```json
{
  "flags": {
    "new_dashboard": {
      "state": "enabled",
      "description": "New React-based dashboard",
      "metadata": {
        "team": "frontend",
        "created": "2025-01-15"
      }
    },
    "experimental_api": {
      "state": "percentage:10",
      "description": "GraphQL API endpoint",
      "metadata": {
        "monitoring": true,
        "rollout_plan": "10% -> 25% -> 50% -> 100%"
      }
    },
    "premium_features": {
      "state": "conditional",
      "description": "Features for premium users",
      "evaluator": "is_premium_user"
    }
  }
}
```

### Loading Configuration

```python
# Load from default location
flags = FeatureFlags.from_file()

# Load from specific file
flags = FeatureFlags.from_file("config/features.json")

# Load with environment variable override
flags = FeatureFlags.from_env_and_file("config/features.json")
```

## Usage Patterns

### Context-Aware Evaluation

```python
# Define context for evaluation
context = {
    "user_id": "user123",
    "user_type": "premium",
    "environment": "production",
    "region": "us-east-1"
}

# Flags can use context for decisions
if flags.is_enabled("premium_features", context=context):
    enable_premium_features()
```

### Conditional Evaluators

```python
# Register a custom evaluator
def is_premium_user(context):
    return context.get("user_type") == "premium"

flags.register_evaluator("is_premium_user", is_premium_user)

# Use in conditional flags
flags.register("premium_features",
               default="conditional",
               evaluator="is_premium_user")
```

### Feature Flag with Metrics

```python
from augint_library.feature_flags import track_usage

@feature_flag("new_checkout", track_usage=True)
def process_checkout(cart):
    """New checkout flow with automatic usage tracking."""
    # Usage is automatically logged
    return checkout_v2(cart)
```

## Advanced Features

### Runtime Updates

```python
# Change flag state at runtime
flags.set_state("new_dashboard", "enabled")

# Temporary override for testing
with flags.override("experimental_api", "enabled"):
    # Flag is enabled only within this context
    test_new_api()
```

### Bulk Operations

```python
# Get all flag states
all_flags = flags.get_all_states()
# Returns: {"new_dashboard": "enabled", "experimental_api": "disabled"}

# Set multiple flags at once
flags.set_states({
    "new_dashboard": "enabled",
    "experimental_api": "percentage:50",
    "premium_features": "conditional"
})
```

### Flag Metadata

```python
# Register with metadata
flags.register("new_feature",
               default="disabled",
               description="Experimental feature for power users",
               metadata={
                   "owner": "platform-team",
                   "jira_ticket": "PLAT-1234",
                   "rollout_date": "2025-02-01"
               })

# Retrieve metadata
info = flags.get_flag_info("new_feature")
print(f"Owner: {info['metadata']['owner']}")
```

## Best Practices

### 1. Naming Conventions

Use descriptive, hierarchical names:
```python
# Good
flags.register("checkout.new_payment_flow")
flags.register("api.graphql.enabled")
flags.register("ui.dark_mode")

# Avoid
flags.register("ff1")
flags.register("test")
```

### 2. Default States

Always provide safe defaults:
```python
# New features default to disabled
flags.register("experimental_feature", default="disabled")

# Rollback features default to enabled
flags.register("legacy_support", default="enabled")
```

### 3. Gradual Rollout

Use percentage rollouts for risky changes:
```python
# Day 1: 5% of users
flags.set_state("new_algorithm", "percentage:5")

# Day 3: No issues, increase to 25%
flags.set_state("new_algorithm", "percentage:25")

# Day 7: Looking good, 50%
flags.set_state("new_algorithm", "percentage:50")

# Day 14: Full rollout
flags.set_state("new_algorithm", "enabled")
```

### 4. Clean Up Old Flags

Remove flags after full rollout:
```python
# After feature is stable and adopted
if flags.exists("old_feature_flag"):
    flags.remove("old_feature_flag")
    # Also remove the flag check from code
```

## CLI Commands

The augint CLI provides commands for managing feature flags:

```bash
# List all flags and their states
augint flags list

# Show detailed info for a flag
augint flags show new_dashboard

# Set a flag state
augint flags set new_dashboard enabled
augint flags set experimental_api percentage:25

# Test a flag with context
augint flags test premium_features --context user_type=premium

# Export current configuration
augint flags export > feature_flags.json

# Import configuration
augint flags import feature_flags.json
```

## Testing with Feature Flags

### Unit Tests

```python
import pytest
from augint_library.feature_flags import FeatureFlags

def test_feature_with_flag_enabled():
    flags = FeatureFlags()

    # Test with flag enabled
    with flags.override("new_feature", "enabled"):
        result = my_function()
        assert result == "new behavior"

    # Test with flag disabled
    with flags.override("new_feature", "disabled"):
        result = my_function()
        assert result == "old behavior"
```

### Integration Tests

```python
@pytest.fixture
def feature_flags():
    """Fixture providing clean feature flags for each test."""
    flags = FeatureFlags()
    # Set up test defaults
    flags.register("test_feature", default="enabled")
    yield flags
    # Clean up
    flags.clear()

def test_api_with_flags(feature_flags):
    # Test with different flag states
    for state in ["enabled", "disabled", "percentage:50"]:
        feature_flags.set_state("api_v2", state)
        response = client.get("/api/endpoint")
        # Assert appropriate behavior
```

### Testing Percentage Rollouts

```python
def test_percentage_rollout():
    flags = FeatureFlags()
    flags.register("gradual_feature", default="percentage:30")

    enabled_count = 0
    total_users = 1000

    for i in range(total_users):
        if flags.is_enabled("gradual_feature", context={"user_id": f"user_{i}"}):
            enabled_count += 1

    # Should be approximately 30% (with some variance)
    percentage = (enabled_count / total_users) * 100
    assert 25 <= percentage <= 35
```

## Migration Guide

If you're adding feature flags to an existing codebase:

1. **Identify Toggle Points**: Find places where behavior should vary
2. **Add Flags Gradually**: Start with one or two flags
3. **Default to Current Behavior**: Ensure flags don't change existing behavior when disabled
4. **Test Both States**: Always test with flags enabled AND disabled
5. **Monitor and Iterate**: Use metrics to guide rollout decisions

Example migration:
```python
# Before
def process_order(order):
    validate_order(order)
    calculate_tax(order)
    charge_payment(order)
    send_confirmation(order)

# After
def process_order(order):
    validate_order(order)

    if flags.is_enabled("new_tax_calculator"):
        calculate_tax_v2(order)
    else:
        calculate_tax(order)

    charge_payment(order)

    if flags.is_enabled("enhanced_notifications"):
        send_enhanced_confirmation(order)
    else:
        send_confirmation(order)
```

## Troubleshooting

### Common Issues

1. **Flag not picking up environment variable**
   - Ensure the variable follows the pattern: `FEATURE_FLAG_YOUR_FLAG_NAME`
   - Check that the flag name in code matches (case-insensitive)

2. **Percentage rollout seems random**
   - Always provide consistent context (like user_id) for deterministic behavior
   - Without context, the decision is random each time

3. **Conditional flags not working**
   - Verify the evaluator function is registered
   - Check that context contains required fields

### Debug Mode

Enable debug logging to troubleshoot:
```python
import logging
logging.getLogger("augint_library.feature_flags").setLevel(logging.DEBUG)

# Now flag decisions will be logged
flags.is_enabled("my_flag", context={"user_id": "123"})
# DEBUG: Evaluating flag 'my_flag' with context {'user_id': '123'}
# DEBUG: Flag 'my_flag' resolved to 'enabled'
```

## Next Steps

- Review the [Feature Flags API Reference](../ai/api/feature-flags-api.md) for detailed API documentation
- See [Feature Flag Patterns](../patterns/feature-flag-patterns.md) for advanced usage patterns
- Check [Performance Considerations](../performance/feature-flags-performance.md) for optimization tips
