# Feature Flags API Reference (AI-Optimized)

## Module: augint_library.feature_flags

### Overview
Lightweight feature flags system for Python applications supporting gradual rollouts, A/B testing, and conditional feature enablement.

### Quick Implementation Path
```python
# Minimal setup
from augint_library.feature_flags import FeatureFlags, feature_flag

flags = FeatureFlags()
flags.register("new_feature", default="disabled")

# Check flag
if flags.is_enabled("new_feature"):
    # new code
else:
    # old code
```

## Core Classes

### FeatureFlags

Main class for managing feature flags.

#### Constructor
```python
FeatureFlags(
    config_file: Optional[str] = None,
    auto_load_env: bool = True
)
```

#### Key Methods

##### register
```python
def register(
    self,
    name: str,
    default: str = "disabled",
    description: Optional[str] = None,
    metadata: Optional[Dict[str, Any]] = None,
    evaluator: Optional[str] = None
) -> None
```
- **name**: Flag identifier (alphanumeric + underscore/hyphen)
- **default**: One of: "enabled", "disabled", "percentage:N", "conditional"
- **evaluator**: Name of registered evaluator function for conditional flags

##### is_enabled
```python
def is_enabled(
    self,
    name: str,
    context: Optional[Dict[str, Any]] = None
) -> bool
```
- **context**: Dictionary with user_id, environment, etc. for consistent evaluation

##### set_state
```python
def set_state(
    self,
    name: str,
    state: str
) -> None
```
- Runtime flag state modification
- State must be valid: "enabled", "disabled", "percentage:N", "conditional"

##### override
```python
@contextmanager
def override(
    self,
    name: str,
    state: str
) -> Iterator[None]
```
- Temporary flag override within context manager

### Flag States

#### State Types
- **"enabled"**: Always returns True
- **"disabled"**: Always returns False  
- **"percentage:N"**: True for N% of evaluations (N = 0-100)
- **"conditional"**: Delegates to evaluator function

#### Percentage Rollout Algorithm
```python
# Consistent hashing for deterministic results
hash_input = f"{flag_name}:{context.get('user_id', random_value)}"
hash_value = hashlib.md5(hash_input.encode()).hexdigest()
user_bucket = int(hash_value[:8], 16) % 100
return user_bucket < percentage
```

## Decorators

### @feature_flag

Conditionally execute functions based on flag state.

```python
@feature_flag(
    flag_name: str,
    fallback: Optional[Callable] = None,
    track_usage: bool = False
)
```

#### Parameters
- **flag_name**: Name of the feature flag to check
- **fallback**: Function to call when flag is disabled
- **track_usage**: Enable automatic usage metrics

#### Usage Examples
```python
# Simple usage
@feature_flag("new_algorithm")
def process_data(data):
    return new_algorithm(data)

# With fallback
@feature_flag("experimental_api", fallback=legacy_api)
def api_endpoint():
    return {"version": "2.0"}

# With usage tracking
@feature_flag("beta_feature", track_usage=True)
def beta_functionality():
    return enhanced_processing()
```

## Configuration

### Environment Variables

Pattern: `FEATURE_FLAG_{NAME}`

```bash
# Examples
export FEATURE_FLAG_NEW_DASHBOARD=enabled
export FEATURE_FLAG_API_V2=percentage:25
export FEATURE_FLAG_PREMIUM=conditional
```

### JSON Configuration

Schema:
```json
{
  "flags": {
    "<flag_name>": {
      "state": "enabled|disabled|percentage:N|conditional",
      "description": "optional description",
      "metadata": {},
      "evaluator": "evaluator_name"
    }
  }
}
```

### Configuration Priority
1. Runtime overrides (set_state, override)
2. Environment variables
3. JSON configuration file
4. Default values from register()

## Conditional Evaluation

### Registering Evaluators
```python
def is_premium_user(context: Dict[str, Any]) -> bool:
    return context.get("user_type") == "premium"

flags.register_evaluator("is_premium_user", is_premium_user)
```

### Context Schema
```python
context = {
    "user_id": str,          # For consistent percentage rollout
    "user_type": str,        # For conditional evaluation
    "environment": str,      # dev/staging/production
    "region": str,           # Geographic region
    "timestamp": datetime,   # Time-based flags
    # Custom fields as needed
}
```

## CLI Interface

### Commands
```bash
# List all flags
augint flags list [--format json|table]

# Show flag details
augint flags show <flag_name>

# Set flag state
augint flags set <flag_name> <state>

# Test flag evaluation
augint flags test <flag_name> [--context key=value]

# Export configuration
augint flags export [--output file.json]

# Import configuration
augint flags import <file.json>

# Clear all flags
augint flags clear [--confirm]
```

## Error Handling

### Exceptions
```python
class FeatureFlagError(AugintError):
    """Base exception for feature flag errors."""

class FlagNotFoundError(FeatureFlagError):
    """Raised when accessing non-existent flag."""

class InvalidFlagStateError(FeatureFlagError):
    """Raised for invalid flag states."""

class EvaluatorNotFoundError(FeatureFlagError):
    """Raised when conditional flag's evaluator is missing."""
```

### Safe Defaults
```python
# Always provide safe defaults
try:
    if flags.is_enabled("unstable_feature"):
        new_behavior()
except FeatureFlagError:
    # Fall back to safe behavior
    old_behavior()
```

## Performance Considerations

### Caching
- Flag states are cached in memory
- File/env reads happen once at startup
- Use `reload()` to refresh configuration

### Overhead
- is_enabled(): ~0.001ms without context
- is_enabled() with percentage: ~0.005ms
- Decorator overhead: ~0.01ms

### Best Practices
```python
# Cache flag state for tight loops
flag_enabled = flags.is_enabled("feature")
for item in large_list:
    if flag_enabled:
        process_new(item)
    else:
        process_old(item)
```

## Testing Utilities

### Mock Fixtures
```python
@pytest.fixture
def mock_flags(monkeypatch):
    """Fixture for testing with feature flags."""
    flags = FeatureFlags()
    monkeypatch.setattr("augint_library.feature_flags._global_flags", flags)
    return flags

def test_with_flag(mock_flags):
    mock_flags.register("test_feature", default="enabled")
    assert my_function() == "expected_behavior"
```

### Testing Helpers
```python
# Test both flag states
def test_feature_both_states():
    for state in ["enabled", "disabled"]:
        with flags.override("feature", state):
            result = function_under_test()
            assert_correct_behavior(result, state)

# Test percentage rollout distribution
def test_percentage_distribution():
    flags.set_state("feature", "percentage:30")
    results = []
    for i in range(1000):
        ctx = {"user_id": f"user_{i}"}
        results.append(flags.is_enabled("feature", ctx))

    enabled_pct = sum(results) / len(results) * 100
    assert 25 <= enabled_pct <= 35  # Allow variance
```

## Integration Examples

### Flask Integration
```python
from flask import g, request

@app.before_request
def setup_feature_flags():
    g.flags = FeatureFlags()
    g.flag_context = {
        "user_id": request.user.id if request.user else None,
        "environment": app.config["ENVIRONMENT"]
    }

@app.route("/api/data")
def get_data():
    if g.flags.is_enabled("new_api", g.flag_context):
        return new_api_response()
    return legacy_api_response()
```

### Django Integration
```python
# middleware.py
class FeatureFlagMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.flags = FeatureFlags()

    def __call__(self, request):
        request.flags = self.flags
        request.flag_context = {
            "user_id": request.user.id if request.user.is_authenticated else None,
            "environment": settings.ENVIRONMENT
        }
        return self.get_response(request)
```

### FastAPI Integration
```python
from fastapi import Depends

async def get_flags() -> FeatureFlags:
    return FeatureFlags()

@app.get("/api/feature")
async def feature_endpoint(
    flags: FeatureFlags = Depends(get_flags),
    user: User = Depends(get_current_user)
):
    context = {"user_id": user.id, "user_type": user.type}
    if flags.is_enabled("new_feature", context):
        return await new_feature_handler()
    return await legacy_handler()
```

## Migration Patterns

### Gradual Rollout Template
```python
# Week 1: 5% canary
flags.set_state("risky_change", "percentage:5")
monitor_metrics()

# Week 2: Expand if metrics good
if metrics_healthy():
    flags.set_state("risky_change", "percentage:25")

# Week 3: Half rollout
flags.set_state("risky_change", "percentage:50")

# Week 4: Full rollout
flags.set_state("risky_change", "enabled")

# Week 8: Remove flag from code
# Delete flag checks and old code path
```

### A/B Testing Pattern
```python
@feature_flag("experiment_checkout_flow")
def checkout(cart):
    # Track conversion with experiment group
    track_event("checkout_completed", {
        "experiment_group": "new_flow",
        "cart_value": cart.total
    })
    return new_checkout_flow(cart)

# Fallback tracks control group
def legacy_checkout(cart):
    track_event("checkout_completed", {
        "experiment_group": "control",
        "cart_value": cart.total
    })
    return old_checkout_flow(cart)
```

## Monitoring and Observability

### Metrics Collection
```python
# Automatic metrics when track_usage=True
@feature_flag("feature", track_usage=True)
def my_feature():
    # Automatically tracks:
    # - feature_flag.evaluated{flag=feature,result=enabled}
    # - feature_flag.execution_time{flag=feature}
    pass
```

### Custom Metrics
```python
class InstrumentedFeatureFlags(FeatureFlags):
    def is_enabled(self, name: str, context: Optional[Dict] = None) -> bool:
        start = time.time()
        result = super().is_enabled(name, context)

        # Record metrics
        metrics.increment("feature_flag.evaluated", tags={
            "flag": name,
            "result": "enabled" if result else "disabled"
        })
        metrics.histogram("feature_flag.evaluation_time",
                         time.time() - start,
                         tags={"flag": name})

        return result
```

### Logging
```python
import logging

# Enable debug logging
logging.getLogger("augint_library.feature_flags").setLevel(logging.DEBUG)

# Logs will show:
# DEBUG: Loading feature flags from environment
# DEBUG: Registered flag 'new_feature' with default 'disabled'
# DEBUG: Flag 'new_feature' evaluated to 'enabled' for context {...}
```

## Thread Safety

The FeatureFlags class is thread-safe for:
- Reading flag states
- Evaluating flags
- Context management

Not thread-safe for:
- Registering new flags
- Setting flag states
- Registering evaluators

Use locking for multi-threaded modifications:
```python
import threading

class ThreadSafeFeatureFlags(FeatureFlags):
    def __init__(self):
        super().__init__()
        self._lock = threading.RLock()

    def register(self, name: str, **kwargs):
        with self._lock:
            super().register(name, **kwargs)

    def set_state(self, name: str, state: str):
        with self._lock:
            super().set_state(name, state)
```
