Feature Toggles with Unleash in Python — Deep Dive

Feature toggles at scale introduce challenges that simple if/else checks do not reveal. Toggle evaluation must be fast and deterministic. Stale toggles accumulate technical debt. Testing all toggle combinations is combinatorially explosive. And the wrong default — failing open vs failing closed — can cause incidents. Production Unleash requires addressing each of these concerns.

SDK architecture

The Python Unleash SDK (UnleashClient) operates on a polling model:

  1. On initialization, the SDK fetches all toggle definitions from the Unleash server.
  2. Toggle definitions are cached in memory as a dictionary.
  3. A background thread polls for updates at a configurable interval (default 15 seconds).
  4. is_enabled() evaluates strategies against the local cache — no network call.
  5. A separate background thread sends usage metrics back to Unleash.
from UnleashClient import UnleashClient

client = UnleashClient(
    url="http://unleash:4242/api",
    app_name="order-service",
    instance_id="pod-abc-123",
    refresh_interval=10,        # seconds between polls
    metrics_interval=60,        # seconds between metric reports
    custom_headers={"Authorization": f"*:{api_token}"},
    cache=FileCache("unleash_cache"),  # Persist cache to survive restarts
)
client.initialize_client()

Failure modes

If the Unleash server is unreachable:

  • On startup: is_enabled() returns the fallback value (default False).
  • During operation: The SDK uses the last successfully fetched cache.
  • With FileCache: The SDK loads the persisted cache from disk, surviving full restarts during outages.

Always specify sensible defaults:

# Operational kill switch — default to enabled (feature ON)
if client.is_enabled("payments-enabled", fallback_function=lambda *a: True):
    process_payment()
else:
    return service_unavailable()

For kill switches, default to enabled. For new features, default to disabled. Getting this wrong means an Unleash outage either disables critical functionality or exposes unfinished features.

Custom strategies

Built-in strategies cover common cases. Custom strategies handle domain-specific logic:

from UnleashClient.strategies import Strategy

class SubscriptionStrategy(Strategy):
    def load_provisioning(self):
        return None
    
    def apply(self, parameters, context):
        allowed_plans = parameters.get("plans", "").split(",")
        user_plan = context.get("properties", {}).get("plan", "")
        return user_plan in allowed_plans

# Register the strategy
client = UnleashClient(
    url="...",
    app_name="order-service",
    custom_strategies={"SubscriptionPlan": SubscriptionStrategy},
)

In the Unleash UI, create a strategy named “SubscriptionPlan” with a parameter “plans” (e.g., “premium,enterprise”). The SDK evaluates this against the context you provide at check time.

Strategy composition

Unleash evaluates multiple strategies with OR logic by default — if any strategy matches, the toggle is enabled. For AND logic, use constraints or segment conditions:

{
    "name": "flexibleRollout",
    "parameters": {"rollout": "50", "groupId": "new-checkout"},
    "constraints": [
        {"contextName": "country", "operator": "IN", "values": ["US", "CA"]},
        {"contextName": "plan", "operator": "NOT_IN", "values": ["free"]}
    ]
}

Constraints narrow down who within a strategy’s target actually gets the feature.

Toggle lifecycle management

Creation to cleanup pipeline

  1. Proposal: Developer creates a toggle in Unleash with type (release/experiment/operational) and expected lifetime.
  2. Development: Code checks the toggle. Both paths (enabled/disabled) have tests.
  3. Rollout: Gradual percentage increase, monitoring error rates and performance.
  4. Full rollout: Toggle enabled for 100%.
  5. Cleanup: Remove the toggle check from code. Delete the toggle from Unleash.

Detecting stale toggles

Unleash marks toggles as “potentially stale” based on their type and age:

  • Release toggles: stale after 40 days
  • Experiment toggles: stale after 40 days
  • Operational toggles: never automatically stale

Automate cleanup reminders:

import httpx
from datetime import datetime, timedelta

def find_stale_toggles():
    response = httpx.get(
        "http://unleash:4242/api/admin/features",
        headers={"Authorization": f"*:{admin_token}"}
    )
    
    stale = []
    for toggle in response.json()["features"]:
        if toggle["stale"] or toggle.get("potentially_stale"):
            stale.append({
                "name": toggle["name"],
                "type": toggle["type"],
                "created": toggle["createdAt"],
            })
    return stale

Run this weekly and post results to Slack.

Testing patterns

Unit testing with overrides

Never let tests depend on the Unleash server. Mock toggle state:

from unittest.mock import patch

def test_new_checkout_enabled():
    with patch.object(client, "is_enabled", return_value=True):
        result = process_checkout(order)
        assert result.used_new_flow

def test_new_checkout_disabled():
    with patch.object(client, "is_enabled", return_value=False):
        result = process_checkout(order)
        assert result.used_old_flow

Integration testing toggle combinations

For critical features, test both paths:

@pytest.mark.parametrize("toggle_state", [True, False])
def test_checkout_both_paths(toggle_state):
    with patch.object(client, "is_enabled", return_value=toggle_state):
        result = process_checkout(order)
        assert result.success

Avoiding combinatorial explosion

With 20 toggles, testing all combinations means 2^20 = 1 million test cases. Instead:

  1. Test each toggle independently (both states).
  2. Identify toggle interactions (rare — most toggles are independent).
  3. Test known interaction pairs explicitly.
  4. Use production monitoring to catch unexpected interactions.

Multi-environment rollout

Unleash supports environment-specific toggle states. A toggle can be enabled in staging but disabled in production:

Toggle: new-payment-flow
├── development: enabled (100%)
├── staging: enabled (100%)
└── production: enabled (10%)

The Python SDK connects with an environment-scoped API token. Each environment sees only its own toggle state:

# Production SDK
prod_client = UnleashClient(
    url="http://unleash:4242/api",
    app_name="order-service",
    custom_headers={"Authorization": f"production:{prod_token}"},
)

# Staging SDK
staging_client = UnleashClient(
    url="http://unleash:4242/api",
    app_name="order-service",
    custom_headers={"Authorization": f"staging:{staging_token}"},
)

Metrics and observability

The SDK reports metrics to Unleash: how many times each toggle was checked, and how many times it evaluated to true vs false. Use this data to:

  • Identify unused toggles (zero checks = safe to delete)
  • Monitor rollout progress (increasing true count as percentage rises)
  • Detect unexpected patterns (a toggle suddenly evaluating differently)

Export Unleash metrics to Prometheus for dashboard integration:

from prometheus_client import Counter

TOGGLE_CHECKS = Counter(
    "feature_toggle_checks_total",
    "Feature toggle evaluations",
    ["toggle_name", "enabled"]
)

def checked_is_enabled(toggle_name, context=None):
    result = client.is_enabled(toggle_name, context)
    TOGGLE_CHECKS.labels(toggle_name=toggle_name, enabled=str(result)).inc()
    return result

Operational patterns

Circuit breaker toggles

Use operational toggles as circuit breakers for external dependencies:

def call_payment_provider(order):
    if not client.is_enabled("payments-circuit-open"):
        try:
            return payment_provider.charge(order)
        except TimeoutError:
            # Could auto-enable the circuit toggle via Unleash API
            raise
    else:
        return enqueue_for_retry(order)

Flip the toggle in the dashboard to instantly bypass a failing payment provider during an incident. No deployment needed.

Maintenance mode

@app.before_request
def check_maintenance():
    if client.is_enabled("maintenance-mode"):
        return render_template("maintenance.html"), 503

Enable from the dashboard for planned maintenance windows.

Self-hosting Unleash

Unleash is open source. Run it alongside your services:

# docker-compose.yml
services:
  unleash:
    image: unleashorg/unleash-server:latest
    environment:
      DATABASE_URL: postgres://unleash:password@db/unleash
    ports:
      - "4242:4242"
  
  db:
    image: postgres:15
    environment:
      POSTGRES_DB: unleash
      POSTGRES_USER: unleash
      POSTGRES_PASSWORD: password

Self-hosted Unleash requires minimal resources: 256 MB RAM, a PostgreSQL instance, and that is it. Backup the database; all toggle state lives there.

One thing to remember: Production feature toggles require disciplined lifecycle management (create, rollout, cleanup), failure-safe defaults, environment-scoped configuration, and observability — the toggle check itself is trivial, but the operational practices around it determine success or technical debt.

pythonunleashfeature-toggles

See Also

  • Python Adaptive Learning Systems How Python builds learning apps that adjust to each student like a personal tutor who knows exactly what you need next.
  • Python Airflow Learn Airflow as a timetable manager that makes sure data tasks run in the right order every day.
  • Python Altair Learn Altair through the idea of drawing charts by describing rules, not by hand-placing every visual element.
  • Python Automated Grading How Python grades homework and exams automatically, from simple answer keys to understanding written essays.
  • Python Batch Vs Stream Processing Batch processing is like doing laundry once a week; stream processing is like a self-cleaning shirt that cleans itself constantly.