Building a Feature Flags API from Scratch with FastAPI
Feature flags are one of those tools that seem simple until you implement them. I recently built FlagBit, a feature flags API, and wanted to share the interesting technical decisions.
Why Another Feature Flags Service?
The market is polarized: LaunchDarkly ($10/seat/mo) for enterprises, Unleash (self-hosted) for teams with DevOps capacity. Nothing in between for small teams that want a hosted API without per-seat pricing.
The Core: Flag Evaluation
The evaluation endpoint is the heart of any feature flags system. Here's the logic:
def evaluate_flag(flag, context):
# 1. Check if flag is enabled at all
if not flag.enabled:
return False, "disabled"
# 2. Check targeting rules
for rule in flag.rules:
if matches_rule(rule, context):
# Percentage rules use consistent hashing
if rule.operator == "percent":
hash_val = consistent_hash(context["user_id"], flag.key)
return hash_val < rule.value, "percentage_rollout"
return True, "targeting_match"
# 3. Fall back to default
return flag.default_enabled, "default"
Consistent Hashing for Percentage Rollouts
The trickiest part is percentage rollouts. If you roll out a feature to 30% of users, each user needs to consistently get the same result. Random sampling won't work — user A might see the feature on one request and not the next.
The solution: hash the user ID + flag key, map it to 0-100:
import hashlib
def consistent_hash(user_id: str, flag_key: str) -> float:
raw = f"{user_id}:{flag_key}"
digest = hashlib.sha256(raw.encode()).hexdigest()
return (int(digest[:8], 16) / 0xFFFFFFFF) * 100
This gives deterministic results: same user + same flag = same hash = same experience. And the distribution is uniform enough for practical purposes.
Targeting Rules Engine
FlagBit supports 5 operators:
-
eq/neq: exact string match -
contains: substring match -
in: value exists in a list -
percent: consistent hash percentage
Rules evaluate against a context object that clients pass with each request:
{
"flag_key": "premium-feature",
"context": {
"user_id": "usr_123",
"plan": "pro",
"country": "US"
}
}
A flag can have multiple rules. First match wins.
API Design Decisions
No SDK required. The entire API is one POST endpoint. Any HTTP client works. This was deliberate — SDKs add complexity, version management, and language lock-in.
SDK keys vs API keys. FlagBit uses two key types: API keys (full access, for management) and SDK keys (read-only, for evaluation). SDK keys are safe to embed in client-side code.
Single evaluation endpoint. Some systems have batch evaluation (evaluate all flags at once). FlagBit keeps it simple: one flag per request. At <50ms per evaluation, this is fast enough for most use cases.
What I Learned
- Consistent hashing is essential for any percentage-based feature. Random sampling causes flickering.
- Rule evaluation order matters. Document it clearly or users will be confused.
- API keys should be hashed in storage. Show the key once at creation, store only the SHA-256 hash.
- SQLite is fine for a feature flags service. The read pattern (evaluate) vastly outweighs writes (flag updates).
Try It
FlagBit is live at flagbit.anethoth.com with a free tier (5 flags, 1 project). The API docs cover everything.
I'm part of the Anethoth product studio — we also build DocuMint (PDF invoice API) and CronPing (cron job monitoring).
Questions or feedback? Drop a comment below.
Top comments (0)