Design: E-Commerce App & Feature Flagging

0/3 in this phase0/35 across the roadmap

📖 Concept

System Design: Large-Scale E-Commerce App

Key Screens & Data Flows:

  • Product catalog with search, filters, and sorting
  • Product detail with variants, reviews, availability
  • Shopping cart with real-time price updates
  • Checkout with payment processing
  • Order tracking with real-time status
  • User profile with order history

Architecture Decisions:

  1. Product catalog: Server-driven UI with local caching. Products cached for offline browsing. Search uses Algolia/ElasticSearch on server. Client caches search results by query.

  2. Shopping cart: Hybrid local + server cart. Anonymous users: local only. Authenticated: synced. Cart merge on login.

  3. Checkout: Cannot be offline — payment requires network. Implement optimistic state with server validation. Handle payment failures gracefully.

  4. Real-time inventory: Subscribe to inventory changes for carted items. Alert user if item goes out of stock during checkout.


Feature Flagging System:

Feature flags are essential for large apps. They enable:

  • Gradual rollouts (1% → 10% → 50% → 100%)
  • A/B testing
  • Kill switches for broken features
  • Platform-specific features (iOS only, Android only)
  • User-segment targeting (beta users, premium, region)

Architecture:

Feature Flag Service (LaunchDarkly, Firebase Remote Config, custom)
  → SDK fetches flags on app start + periodic refresh
  → Flags cached locally for offline access
  → Components conditionally render based on flags
  → Analytics tied to flag variants for measurement

💻 Code Example

codeTap to expand ⛶
1// === FEATURE FLAG SYSTEM ===
2
3interface FeatureFlag {
4 key: string;
5 enabled: boolean;
6 variant?: string; // For A/B tests
7 payload?: any; // Custom config per flag
8}
9
10interface FeatureFlagUser {
11 id: string;
12 email?: string;
13 plan?: 'free' | 'premium' | 'enterprise';
14 platform: 'ios' | 'android';
15 appVersion: string;
16 country?: string;
17}
18
19class FeatureFlagService {
20 private flags: Map<string, FeatureFlag> = new Map();
21 private user: FeatureFlagUser;
22 private refreshInterval: ReturnType<typeof setInterval>;
23
24 async initialize(user: FeatureFlagUser) {
25 this.user = user;
26
27 // Load cached flags immediately (for instant startup)
28 const cached = await AsyncStorage.getItem('feature_flags');
29 if (cached) {
30 this.flags = new Map(JSON.parse(cached));
31 }
32
33 // Fetch fresh flags in background
34 await this.refresh();
35
36 // Periodic refresh every 5 minutes
37 this.refreshInterval = setInterval(() => this.refresh(), 5 * 60 * 1000);
38 }
39
40 private async refresh() {
41 try {
42 const response = await api.getFeatureFlags({
43 userId: this.user.id,
44 platform: this.user.platform,
45 appVersion: this.user.appVersion,
46 });
47
48 this.flags = new Map(response.flags.map(f => [f.key, f]));
49
50 // Cache for offline
51 await AsyncStorage.setItem(
52 'feature_flags',
53 JSON.stringify([...this.flags])
54 );
55 } catch (error) {
56 // Fail silently — use cached flags
57 console.warn('Failed to refresh feature flags:', error);
58 }
59 }
60
61 isEnabled(key: string, defaultValue = false): boolean {
62 return this.flags.get(key)?.enabled ?? defaultValue;
63 }
64
65 getVariant(key: string): string | undefined {
66 return this.flags.get(key)?.variant;
67 }
68
69 getPayload<T>(key: string): T | undefined {
70 return this.flags.get(key)?.payload as T;
71 }
72}
73
74// React hook for feature flags
75function useFeatureFlag(key: string, defaultValue = false): boolean {
76 const flagService = useContext(FeatureFlagContext);
77 return flagService.isEnabled(key, defaultValue);
78}
79
80// Usage in components
81function CheckoutScreen() {
82 const showNewPaymentUI = useFeatureFlag('new_payment_ui');
83 const showApplePay = useFeatureFlag('apple_pay_enabled');
84
85 return (
86 <View>
87 {showNewPaymentUI ? <NewPaymentForm /> : <LegacyPaymentForm />}
88 {showApplePay && Platform.OS === 'ios' && <ApplePayButton />}
89 </View>
90 );
91}
92
93// === E-COMMERCE: Cart Sync Architecture ===
94
95interface CartItem {
96 productId: string;
97 variantId: string;
98 quantity: number;
99 addedAt: number;
100 price: number; // Snapshot at add time
101}
102
103class CartService {
104 // Merge anonymous local cart with server cart on login
105 async mergeCartsOnLogin(localCart: CartItem[], serverCart: CartItem[]) {
106 const merged = new Map<string, CartItem>();
107
108 // Server cart is base
109 for (const item of serverCart) {
110 merged.set(item.variantId, item);
111 }
112
113 // Local cart additions
114 for (const item of localCart) {
115 if (merged.has(item.variantId)) {
116 // Same item in both — take higher quantity
117 const existing = merged.get(item.variantId)!;
118 merged.set(item.variantId, {
119 ...existing,
120 quantity: Math.max(existing.quantity, item.quantity),
121 });
122 } else {
123 merged.set(item.variantId, item);
124 }
125 }
126
127 // Validate prices (may have changed)
128 const validatedCart = await this.validatePrices([...merged.values()]);
129
130 // Sync merged cart to server
131 await api.updateCart(validatedCart);
132
133 return validatedCart;
134 }
135}

🏋️ Practice Exercise

System Design Exercises:

  1. Design the product catalog with offline browsing, search, and filtering
  2. Implement a feature flag hook with A/B test variant tracking
  3. Design the cart sync logic for a multi-device e-commerce app
  4. Build a checkout flow that handles payment failures, retries, and 3D Secure
  5. Design the notification delivery system for order status updates
  6. Create an analytics pipeline that tracks user journeys through the purchase funnel

⚠️ Common Mistakes

  • Making the checkout flow depend on local state only — cart validation and price verification must happen server-side before payment

  • Not caching feature flags locally — app shouldn't require network to decide what to show; cache on every successful fetch

  • Allowing feature flag evaluation to be async — flag checks should be synchronous from cache; async fetching happens in background

  • Not considering cart abandonment — design for interrupted checkouts, save state, and send recovery notifications

  • Hardcoding feature toggles as if/else — use a proper flag service that supports gradual rollout and instant kill switches

💼 Interview Questions

🎤 Mock Interview

Mock interview is powered by AI for Design: E-Commerce App & Feature Flagging. Login to unlock this feature.