how-to

Feature Flags in Python, Done Right - A 2026 Tutorial for Flask and Django

A hands-on guide to implementing feature flags in Python, from a hand-rolled dict toggle to production SDKs in Flask and Django. Real illustrative code and honest trade-offs.

Published:

Feature flags in Python are quick to start and easy to outgrow. The naive version - a module-level boolean you change with a redeploy - works right up until the day you need to turn something off without shipping code. This tutorial walks the whole path, from a dict you can write in five minutes to production SDKs wired into Flask and Django, with real illustrative code and the honest trade-off at each step.

The core idea is to separate is this code deployed from is this feature on. Once those are two independent switches, you can deploy dark, roll out gradually, and kill a bad feature instantly. And Python has a specific advantage worth knowing up front - because Python flags almost always evaluate server-side, they dodge the client-side billing traps that catch browser apps, and self-hosting is genuinely practical since your team already runs a database and a web stack. For a ranked tool comparison rather than a build walkthrough, see best feature flag tools for Python.

Step 1 - Roll your own for a few flags

Before reaching for a platform, know what you are replacing. For one or two flags that rarely change, this is genuinely enough. The following code is illustrative.

# flags.py
import os

FLAGS = {
    "new_checkout": os.getenv("FLAG_NEW_CHECKOUT", "false") == "true",
    "fast_search": os.getenv("FLAG_FAST_SEARCH", "false") == "true",
}

def is_enabled(name: str) -> bool:
    return FLAGS.get(name, False)

Then at the call site:

from flags import is_enabled

if is_enabled("new_checkout"):
    return render_new_checkout(request)
return render_legacy_checkout(request)

What you get is zero dependencies, instant reads, and full control. What you lose is everything that makes flags powerful - you cannot change a flag without a restart, there is no per-user targeting, no percentage rollout, no audit log, and no UI for a non-engineer to flip. The moment you want any of those, a platform pays for itself. Our feature flag management guide covers exactly when that line gets crossed.

Step 2 - Add per-user targeting yourself

The first thing you outgrow is the on-or-off switch. A percentage rollout needs deterministic per-user assignment - the same user must always land in the same bucket, or they flicker between variants on every request. A hash does this cleanly. Illustrative:

import hashlib

def in_rollout(flag: str, user_id: str, percent: int) -> bool:
    key = f"{flag}:{user_id}".encode()
    bucket = int(hashlib.md5(key).hexdigest(), 16) % 100
    return bucket < percent

Now in_rollout("new_checkout", user.id, 5) puts a stable 5 percent of users into the new checkout. This is the mechanism every platform implements internally - understanding rollout percentage and deterministic bucketing here makes the SDKs later far less mysterious. But you still cannot change that 5 without a redeploy, and that limitation is what pushes teams to a real platform.

Step 3 - Wire a managed SDK into Flask

Once you need runtime changes, targeting rules, and an audit trail, a platform SDK replaces your hand-rolled code. The pattern is the same across vendors - initialize a client once at startup, evaluate per request. Here is the shape in Flask, illustrative and vendor-neutral:

from flask import Flask, g, request

app = Flask(__name__)
# Initialize the flag client ONCE at startup, not per request.
flag_client = create_flag_client(sdk_key="YOUR_SDK_KEY")

@app.route("/checkout")
def checkout():
    user = {"key": current_user_id(), "email": current_user_email()}
    if flag_client.is_enabled("new_checkout", user):
        return render_new_checkout()
    return render_legacy_checkout()

The one rule that matters for performance - create the client once and reuse it. A well-built SDK caches flag rules in memory and evaluates them locally in microseconds, so a flag check is a dictionary lookup, not an API call. Creating a client per request throws that away and adds a network round trip to every request.

Step 4 - The Django angle

Django deserves its own note because the framework’s structure gives you a natural home for the client. Initialize it in an AppConfig.ready() hook so it starts with the app, then evaluate in views, middleware, or templates.

# apps.py
from django.apps import AppConfig

class CoreConfig(AppConfig):
    name = "core"
    def ready(self):
        from core.flags import init_flags
        init_flags()  # one client for the process

There is a pleasing symmetry worth calling out. Flagsmith - one of the leading open-source options - is itself built on Django and Postgres. So a Django team that self-hosts Flagsmith is running the exact architecture it already operates, and Flagsmith ships a Python SDK among its 15-plus open-source SDKs under a BSD-3-Clause license. The honest trade is the DevOps - running the stack in production is real work.

Step 5 - Pick a platform, and know why

The right platform depends on whether you want to self-host and how you want to be billed. Server-side Python evaluation matters here - it does not automatically trigger a client-side MAU meter, so the pricing math is usually kinder than for browser apps.

ToolBest for a Python teamLicenseSelf-hostStarting price
FlagsmithOpen-source flags on a Django and Postgres stackBSD-3-ClauseYes$0 / $40 mo annual
UnleashSelf-hosting in your own infra, incl. air-gappedAGPL-3.0Yes$0 self-hosted
ConfigCatFlat pricing, no per-MAU feeProprietaryNo$0 free / $110 mo
GrowthBookWarehouse-native experimentation with flagsMIT coreYes$0 self-hosted
LaunchDarklyDeepest targeting, pay to skip opsProprietaryNoUsage-based

A few Python-specific notes. Unleash is the self-hosting purist’s pick with a Python SDK and air-gapped support, but RBAC, SSO and SCIM are gated to paid tiers. ConfigCat charges no per-seat and no per-MAU fee - you pay by config-download volume instead - which is predictable until a chatty SDK crosses a traffic tier, so cache well. GrowthBook adds a real stats engine if you want experimentation alongside flags and already have an instrumented warehouse. LaunchDarkly has the deepest targeting and a Python SDK, but bills $10 per service connection per month, which multiplies across a microservice backend even though server-side Python avoids the client-side MAU charge.

The short version

  • Start with a dict read from the environment - for one or two flags you do not need a library.
  • Add deterministic hashing for percentage rollouts, which teaches you what every SDK does internally.
  • Move to a managed SDK when you need runtime changes, targeting, and an audit trail - and initialize the client once, never per request.
  • Django teams have an edge self-hosting Flagsmith, since it runs the same Django and Postgres stack they already know.
  • Server-side evaluation is a pricing advantage - Python flags usually dodge the client-side MAU meter, but model each vendor’s other meters before committing.

Python is one of the better languages to run flags in because the ops skills to self-host are already in the room. For the full lifecycle from first flag onward, see how to implement feature flags and feature flag best practices. Prices verified against each vendor’s tool page on 28 July 2026.

Frequently Asked Questions

What is the best feature flag library for Python?

It depends on whether you want to self-host. Flagsmith ships a Python SDK, is open source under BSD-3-Clause, and its own backend is a Django and Postgres stack, so a Python team self-hosts a familiar architecture. Unleash is the self-hosting purist's pick under AGPL-3.0 with a Python SDK, though SSO and RBAC are Enterprise-gated. LaunchDarkly has a Python SDK and the deepest targeting if you can absorb usage-based pricing. ConfigCat offers a Python SDK with flat pricing and no per-MAU fee. For one toggle with no vendor, a dict read from an environment variable is enough.

Can I implement feature flags in Python without a third-party service?

Yes. For a handful of flags a dictionary read from an environment variable or config file works fine, and this guide shows that pattern first. What you give up is runtime updates without a redeploy, per-user targeting, percentage rollouts, and an audit trail. The moment you need to flip a flag at 2am without shipping code, or roll out to 5 percent of users, a managed SDK or a self-hosted server earns its keep.

How do Python feature flag SDKs work?

Most Python SDKs initialize a client once at startup with an SDK key, fetch flag definitions, and cache them in memory. Evaluation then happens locally per request - you pass a user identifier and the client decides the flag without a network hop on the hot path. A background thread polls or streams updates so changes made in the dashboard reach your app within seconds. The key rule is to create the client once at application startup and reuse it, never per request.

Does server-side Python evaluation change feature flag pricing?

Often, yes, in your favor. Python flags usually evaluate server-side - in a Django view, a Flask route, a Celery task - which does not automatically trigger the client-side monthly-active-user meter that catches browser apps. On LaunchDarkly, for example, server-side use avoids the client-side MAU charge, though the $10 per service connection per month still applies and multiplies across microservices. Model your specific meters before committing, since server-side does not mean free.

Explore More

Free Newsletter

Get the Feature Flags Newsletter

Platform benchmarks, real pricing data and progressive delivery practice. No spam.

Free. Unsubscribe any time. See our privacy policy.

Related Articles