Feature Flags in Python: Django, FastAPI & Flask Guide
Did you know that 70 % of production incidents are caused by new code that wasn’t fully tested in the wild? Feature flags let you ship, test, and roll‑back code in seconds, not days—making every Python web‑app (Django, FastAPI, Flask) safer and more agile.What Are Feature Flags and Why They Matter for Python Projects
A feature flag is a lightweight toggle that lets you turn a piece of code on or off at runtime. Think of it as a remote switch for your application’s behavior. *Why?* Because real‑world deployments rarely happen in a vacuum. When you push a new dashboard to a small group of users, you can monitor clicks, errors, and churn before opening the door to everyone. *Business impact:* Faster releases, A/B testing, risk mitigation, and even GDPR compliance—imagine quickly turning off a data‑collecting feature if a user files a request. *Examples:* Netflix rolls out new recommendation engines gradually; Shopify tests checkout flows with a 1 % slice; a fintech startup avoided a $1 M outage by instantly disabling a buggy payment processor via a flag. In my experience, the biggest win is the *speed* you gain in the feedback loop—developers can ship without waiting for a full CI cycle, and product teams can pivot with confidence.Setting Up a Minimal Flag System with django-waffle (Django)
First, install the package.pip install django-waffle
Add `'waffle'` to `INSTALLED_APPS` in `settings.py` and run the migrations.
python manage.py migrate waffle
Now create a flag via the admin or a data migration. In a view, you can read it like this:
from waffle import flag_is_active
def dashboard(request):
if flag_is_active(request, 'new_dashboard'):
return render(request, 'new_dashboard.html')
return render(request, 'old_dashboard.html')
In the admin, you can set the flag to be active only for staff. That way, the rest of the team sees the old UI while you test the new design with privileged users.
*Tip:* Keep flag definitions in a dedicated migration file. This makes it easy to version‑control and roll back if something goes wrong.
Feature Flags in FastAPI Using fastapi-featureflags
FastAPI thrives on async and dependency injection. `fastapi-featureflags` fits right in. Install:pip install fastapi-featureflags
Define flags in a Pydantic settings class:
from fastapi import FastAPI, Depends
from fastapi_featureflags import FeatureFlags, Flag
class Settings(Flag):
beta_mode: bool = False
flags = FeatureFlags(settings=Settings)
app = FastAPI()
@app.get("/data")
def get_data(flag: bool = Depends(flags.dependency("beta_mode"))):
if flag:
return {"status": "beta", "data": {"feature": "new"}}
return {"status": "stable", "data": {"feature": "old"}}
That endpoint now returns a different JSON payload when `beta_mode` is true. To flip the flag, simply change the environment variable `BETA_MODE=true` and reload the app—no code changes required.
Flask Integration with flask-featureflags & Custom Middleware
Flask is flexible, but you still want a clean way to expose flags throughout the request. Install:pip install flask-featureflags
Register the extension and point it to a Redis backend or a JSON file:
from flask import Flask, g
from flask_featureflags import FeatureFlags
app = Flask(__name__)
app.config['FEATURE_FLAGS_SOURCE'] = 'redis://localhost:6379/0'
feature_flags = FeatureFlags(app)
@app.before_request
def load_flags():
g.flags = feature_flags.get_flags()
Now, in any route, you can check a flag:
@app.route('/auth')
def auth():
if g.flags.get('new_auth_flow') and request.user.email.endswith('@example.com'):
return render_template('new_auth.html')
return render_template('login.html')
The middleware pattern keeps the flag object handy for the entire request lifecycle.
Actionable Takeaways & Best‑Practice Checklist
- Version‑control definitions. Store flag names and defaults in a `flags.yml` or as part of the migrations.
- Set expirations. Add a `expires_at` column or metadata so stale flags get cleaned up automatically.
- Analytics integration. Export usage logs to CSV and load them into a Jupyter notebook. With pandas, group by flag state to see conversion rates; use numpy for t‑tests.
- Deployment hygiene. Run `pip freeze > requirements.txt` before every release. Add a lint rule to enforce snake_case flag names.
- Monitoring. Hook flag changes into alerts. If a flag goes from off to on in production, you might want a quick smoke test.
Frequently Asked Questions
How do I add feature flags to an existing Django project without breaking migrations?
Install django-waffle, run python manage.py migrate waffle, then add flags via the admin or a data migration. Because flags live in their own tables, they don’t interfere with your existing schema.
Can I use the same flag configuration across Django, FastAPI, and Flask?
Yes—store flags in a central source such as Redis or a JSON file, and let each framework’s extension read from it. This keeps the toggle logic consistent while each app uses its native integration.
What is the difference between a “feature flag” and a “configuration flag” in Python?
A feature flag controls behavior (e.g., show new UI) and is usually toggled at runtime, while a configuration flag sets environmental parameters (e.g., DB URL) and is read at startup. Feature flags are meant for gradual rollouts and quick rollbacks.
How do I monitor the impact of a flag rollout using pandas or numpy?
Export the usage logs to CSV, load them into a Jupyter notebook, and use pandas to group by flag state and calculate conversion or error rates. Numpy can help with statistical significance testing (e.g., t‑test).
Is it safe to keep feature flags in code repositories?
Store the definition (name, description, default) in code, but keep the state (on/off per environment) in a secure store like Redis, AWS Parameter Store, or environment variables. This avoids leaking production toggles in source control.
Related reading: Original discussion
What do you think?
Have experience with this topic? Drop your thoughts in the comments - I read every single one and love hearing different perspectives!
Comments
Post a Comment