Back to Journal
SaaS Engineering

Feature Flag Architecture Best Practices for Startup Teams

Battle-tested best practices for Feature Flag Architecture tailored to Startup teams, including anti-patterns to avoid and a ready-to-use checklist.

Muneer Puthiya Purayil 17 min read

Startup teams need feature flags that ship fast and don't become technical debt. Unlike enterprise flag systems built around compliance and multi-tenant isolation, startup feature flags should be lightweight, opinionated, and ruthlessly pruned. These practices come from building feature flag systems at early-stage companies where every engineering hour counts.

Core Architecture for Startups

Keep it simple: a JSON configuration file synced to your application, evaluated locally. No separate flag service, no database, no infrastructure to manage until you outgrow it.

typescript
1// flags.json — committed to your repo or served from S3
2{
3 "new-onboarding-flow": {
4 "enabled": true,
5 "percentage": 25,
6 "allowList": ["user-123", "user-456"],
7 "createdAt": "2025-01-15",
8 "owner": "sarah",
9 "type": "release",
10 "expiresAt": "2025-03-15"
11 },
12 "premium-analytics": {
13 "enabled": true,
14 "percentage": 100,
15 "plans": ["pro", "enterprise"],
16 "type": "permission"
17 }
18}
19 
typescript
1import { createHash } from "crypto";
2import flags from "./flags.json";
3 
4interface FlagConfig {
5 enabled: boolean;
6 percentage?: number;
7 allowList?: string[];
8 plans?: string[];
9 type: "release" | "experiment" | "permission" | "ops";
10 expiresAt?: string;
11}
12 
13function isEnabled(flagKey: string, context: { userId: string; plan?: string }): boolean {
14 const flag = (flags as Record<string, FlagConfig>)[flagKey];
15 if (!flag || !flag.enabled) return false;
16 
17 // Expired flags return false
18 if (flag.expiresAt && new Date(flag.expiresAt) < new Date()) return false;
19 
20 // Allow list takes priority
21 if (flag.allowList?.includes(context.userId)) return true;
22 
23 // Plan-based gating
24 if (flag.plans && context.plan) {
25 if (!flag.plans.includes(context.plan)) return false;
26 }
27 
28 // Percentage rollout
29 if (flag.percentage !== undefined && flag.percentage < 100) {
30 const hash = createHash("sha256").update(`${flagKey}:${context.userId}`).digest();
31 const bucket = (hash.readUInt32BE(0) / 0xffffffff) * 100;
32 return bucket < flag.percentage;
33 }
34 
35 return true;
36}
37 

This covers 90% of startup flag needs in under 50 lines. No SDK, no vendor, no infrastructure.

Best Practices

1. Every Flag Gets a Removal Date

The number one failure mode of feature flags at startups is accumulation. Two years later, nobody knows what enable-new-thing-v2-final does.

typescript
1// Enforce in CI
2const MAX_RELEASE_FLAG_DAYS = 30;
3const MAX_EXPERIMENT_FLAG_DAYS = 45;
4 
5function checkFlagExpiry(flags: Record<string, FlagConfig>): string[] {
6 const violations: string[] = [];
7 const now = Date.now();
8 
9 for (const [key, flag] of Object.entries(flags)) {
10 if (flag.type === "ops" || flag.type === "permission") continue;
11 
12 if (!flag.expiresAt) {
13 violations.push(`${key}: missing expiresAt`);
14 continue;
15 }
16 
17 const maxDays = flag.type === "release" ? MAX_RELEASE_FLAG_DAYS : MAX_EXPERIMENT_FLAG_DAYS;
18 const created = new Date(flag.createdAt).getTime();
19 const age = (now - created) / 86400000;
20 
21 if (age > maxDays && flag.enabled) {
22 violations.push(`${key}: ${Math.floor(age)} days old, max ${maxDays}`);
23 }
24 }
25 
26 return violations;
27}
28 

2. Use Feature Flags for Trunk-Based Development

Startups should practice trunk-based development. Feature flags replace long-lived feature branches:

typescript
1// Wrap incomplete features in flags — merge to main daily
2export function CheckoutPage() {
3 const { userId } = useAuth();
4 const showNewCheckout = isEnabled("new-checkout-flow", { userId });
5 
6 if (showNewCheckout) {
7 return <NewCheckoutFlow />;
8 }
9 return <LegacyCheckoutFlow />;
10}
11 

This approach means every developer merges to main at least daily. The flag prevents incomplete work from affecting users. When the feature is complete, ramp the percentage from 0 → 10 → 50 → 100, then remove the flag.

3. Three Flags Maximum Per Feature

If a feature requires more than three flags, you're over-decomposing. A typical feature needs:

  1. The feature flag — gates the entire feature
  2. An experiment flag — if you're A/B testing a specific aspect
  3. A kill switch — for emergency disable
typescript
1// Good: one flag per feature
2if (isEnabled("ai-suggestions", ctx)) {
3 showAISuggestions();
4}
5 
6// Bad: flag per implementation detail
7if (isEnabled("ai-suggestions-ui", ctx) &&
8 isEnabled("ai-suggestions-backend", ctx) &&
9 isEnabled("ai-suggestions-model-v2", ctx) &&
10 isEnabled("ai-suggestions-cache", ctx)) {
11 showAISuggestions();
12}
13 

4. Log Flag Evaluations for Debugging

When something goes wrong, you need to know which flags were active:

typescript
1function isEnabled(flagKey: string, context: EvalContext): boolean {
2 const result = evaluateFlag(flagKey, context);
3
4 // Attach to request context for debugging
5 context.evaluatedFlags = context.evaluatedFlags || {};
6 context.evaluatedFlags[flagKey] = result;
7
8 return result;
9}
10 
11// In your error handler
12app.use((err, req, res, next) => {
13 logger.error({
14 error: err.message,
15 flags: req.evaluatedFlags, // Which flags were active when the error occurred
16 userId: req.userId,
17 });
18 next(err);
19});
20 

5. Graduated Rollout Template

Every startup should have a standard rollout playbook:

DayPercentageAction
00%Merge to main, flag disabled
1Internal team onlyQA in production
35%Monitor error rates and performance
525%Check user feedback channels
750%Verify metrics match expectations
10100%Full rollout
14Remove flagClean up code

Need a second opinion on your saas engineering architecture?

I run free 30-minute strategy calls for engineering teams tackling this exact problem.

Book a Free Call

Anti-Patterns to Avoid

  1. Using flags instead of fixing bugs — "just flag it off" becomes a crutch for avoiding root cause analysis.

  2. Nested flag conditions — if you're checking flagA && flagB && !flagC, your flags are too coupled. Simplify to a single flag.

  3. Flag-driven architecture — building your entire routing or business logic around flag states. Flags should wrap features, not define architecture.

  4. No monitoring — shipping behind a flag without monitoring is worse than shipping without a flag, because you've added complexity without the visibility to use it effectively.

  5. Enterprise-grade infrastructure too early — you don't need LaunchDarkly on day one. A JSON file in your repo or a simple Redis-backed service covers most needs until Series B.

Checklist

  • Feature flags are used for trunk-based development (no long-lived branches)
  • Every release flag has an expiration date within 30 days
  • CI check fails on expired flags
  • Maximum 3 flags per feature
  • Flag evaluations are logged and available in error context
  • Standard rollout template is documented and followed
  • Flags are removed within 2 weeks of full rollout
  • Allow-list exists for internal testing before public rollout
  • Percentage rollouts use deterministic hashing for consistency
  • Monthly flag cleanup session is on the engineering calendar

Conclusion

The best feature flag system for a startup is the simplest one that prevents broken features from reaching users. A JSON configuration file with percentage-based rollouts, deterministic bucketing, and expiration dates handles the needs of a 5-30 person engineering team. The discipline of removing flags matters more than the sophistication of the system — invest your engineering time in building product, not building flag infrastructure.

Graduate to a dedicated flag service (Unleash, Flagsmith, or a vendor like LaunchDarkly) when you hit 50+ engineers, need per-environment configuration, or when the JSON file approach creates merge conflicts frequently. Until then, keep it simple and keep it clean.

FAQ

Need expert help?

Building with saas engineering?

I help teams ship production-grade systems. From architecture review to hands-on builds.

Muneer Puthiya Purayil

SaaS Architect & AI Systems Engineer. 10+ years shipping production infrastructure across fintech, automotive, e-commerce, and healthcare.

Engage

Start a
Conversation.

For teams building at scale: SaaS platforms, agentic AI systems, and enterprise mobile infrastructure. Scope and fit are evaluated before any engagement begins.

Limited availability · Q3 / Q4 2026