Ship to production continuously without big-bang risk. Build a feature-flag layer in Django, roll features out to a percentage of users, run A/B experiments, and add instant kill switches for when something goes wrong.
Deploying code and releasing a feature should be two separate events. Feature flags let you merge to main, deploy on Tuesday, and turn a feature on for 5% of users on Thursday — then roll it back in one click if metrics dip. This is progressive delivery, and it's how mature teams ship fast without breaking things.
Without flags, every deploy is a gamble — all code goes live to everyone at once. With flags, the deploy is boring (the new code is dormant) and the release is a controlled, reversible config change. That separation is the entire point.
class FeatureFlag(models.Model):
key = models.SlugField(unique=True)
enabled = models.BooleanField(default=False)
rollout_percent = models.PositiveSmallIntegerField(default=0) # 0-100
allowed_users = models.ManyToManyField(User, blank=True)
Resolve a flag deterministically — the same user always gets the same answer, which is essential for a coherent experience and honest A/B data:
import hashlib
def flag_enabled(key, user=None):
flag = FeatureFlag.objects.filter(key=key).first()
if not flag or not flag.enabled:
return False
if user and flag.allowed_users.filter(pk=user.pk).exists():
return True
if flag.rollout_percent >= 100:
return True
if not user:
return False
bucket = int(hashlib.sha256(
f"{key}:{user.pk}".encode()).hexdigest(), 16) % 100
return bucket < flag.rollout_percent
Hashing key:user_id means user 42 stays in the same bucket as you ramp 5% → 25% → 100%, and bucketing differs per flag.
Ramp gradually while watching error rates and latency: 1% → 5% → 25% → 50% → 100%, pausing at each step. If your dashboards stay green, continue; if they spike, drop back to 0 instantly.
The same deterministic bucketing powers experiments. Assign users to variant A or B, emit an analytics event with the variant, and measure the metric you actually care about (conversion, retention) — not vanity clicks. Run until you reach statistical significance, then ship the winner and delete the flag.
Feature flags turn scary releases into boring config changes. Build a small deterministic flag layer (hash key:user_id so rollouts are stable), ramp features as canaries while watching metrics, reuse the bucketing for A/B tests, and keep kill switches on anything risky. The discipline that keeps it healthy: cache flag lookups and ruthlessly delete flags once they've fully shipped.