Feature Flags Without a Third-Party Service
You do not need LaunchDarkly to ship features safely. A simple flag system backed by a database or config file gets you 90% of the way.
Basic Flag Store
interface FeatureFlag {
name: string;
enabled: boolean;
rolloutPercent: number; // 0-100
allowedUsers?: string[]; // whitelist for beta testers
}
class FeatureFlagService {
private flags = new Map();
async load(): Promise {
const rows = await db.query("SELECT * FROM feature_flags");
rows.forEach((r) => this.flags.set(r.name, r));
}
isEnabled(name: string, userId?: string): boolean {
const flag = this.flags.get(name);
if (!flag || !flag.enabled) return false;
if (flag.allowedUsers?.includes(userId!)) return true;
const hash = simpleHash(${name}-${userId}) % 100;
return hash < flag.rolloutPercent;
}
}
## Usage in Routes
typescript
app.get("/dashboard", async (req, res) => {
if (features.isEnabled("new-dashboard", req.user.id)) {
return res.render("dashboard-v2");
}
res.render("dashboard");
});
## Gradual Rollout Strategy
1. Enable for internal users (`allowedUsers` list)
2. Roll out to 10% of users
3. Monitor error rates and performance
4. Increase to 50%, then 100%
5. Remove the flag and old code path
## Cleanup Matters
Dead flags are tech debt. Set a rule: every flag gets a cleanup date. If a flag has been at 100% for 30 days, remove it and the old code path.
***
*Part of my Production Backend Patterns series. Follow for more practical backend engineering.*
Top comments (0)