Quest 114 - Feature Flag System
Quest 114: Feature Flag System
medium 25-30 minutes🎯 Learning Objectives
- How feature flags enable safe, gradual rollouts of new features
- Why deterministic hashing (not Math.random) is essential for consistent user experience
- How to implement percentage-based rollouts with rule-based targeting
- The engineering habit of shipping small, testing with real users before full rollout
📖 Concept: Feature Flags for Safe Deployment
ทุกคนเคย experience แบบนี้ — deploy feature ใหม่, production ล่ม, rollback ทั้งระบบ Feature Flags คือ solution: แยก code deployment จาก feature activation
Deploy code ──────→ code is live (but hidden behind flag) │ ▼Enable flag ──────→ feature appears for users │ ▼Gradual rollout ──→ 10% → 50% → 100% │ ▼Disable flag ─────→ instant rollback (no redeploy!)ทำไม Feature Flags ถึง Powerful?
- Separation — deploy code ได้โดยไม่ต้องเปิด feature ให้ user
- Gradual Rollout — เปิดให้ 10% ของ user ก่อน เพื่อ catch issues
- Instant Rollback — ปิด flag ทันที ไม่ต้อง redeploy
- A/B Testing — ใช้ flags เปรียบเทียบ user experience
Rule-Based Targeting
นอกจาก percentage-based rollout ยังมี rules ที่เปิด feature ให้ user เฉพาะกลุ่ม เช่น:
- เปิดให้ staging environment ก่อน production
- เปิดให้ internal users ก่อน public
- เปิดให้ users ใน特定 region
⚙️ How It Works
Two Functions
const flag = createFeatureFlag('new-ui', { percentage: 50 });const result = evaluateFlag(flag, { userId: 'user-1', environment: 'production' });// { enabled: true/false, reason: '...' }createFeatureFlag(name, config)
สร้าง flag object ที่มี:
name— ชื่อ flagconfig— settings:percentage— % ของ users ที่เปิด (0-100)rules— rule-based targeting array
evaluateFlag(flag, context)
ตัดสินใจว่า flag เปิดสำหรับ user นี้หรือไม่:
1. Check rules ก่อน │ ├─ Rule match? → enabled = true │ ▼2. Check percentage (deterministic hash) │ ├─ hash(userId) % 100 < percentage? → enabled = true │ ▼3. Default → enabled = falseDeterministic Hashing — สำคัญมาก
// ❌ WRONG: Math.random() — user เปลี่ยนผลทุกครั้ง!evaluateFlag(flag, ctx1); // trueevaluateFlag(flag, ctx1); // false — ผลเปลี่ยน!
// ✅ CORRECT: hash(userId) — user เดียวกันได้ผลเดิมเสมอfunction simpleHash(str) { let hash = 0; for (let i = 0; i < str.length; i++) { hash = (hash * 31 + str.charCodeAt(i)) % 100; } return hash;}evaluateFlag(flag, ctx1); // trueevaluateFlag(flag, ctx1); // true — ผลเดิม!💡 Example: Gradual Rollout
Create flags:
// 100% rollout — ทุก user เห็นconst fullFlag = createFeatureFlag('full', { percentage: 100 });evaluateFlag(fullFlag, { userId: 'u1', environment: 'production' });// { enabled: true, reason: '...' }
// 0% rollout — ไม่มี user เห็นconst zeroFlag = createFeatureFlag('zero', { percentage: 0 });evaluateFlag(zeroFlag, { userId: 'u1', environment: 'production' });// { enabled: false, reason: '...' }
// 50% rollout — ประมาณครึ่งหนึ่งconst flag50 = createFeatureFlag('test50', { percentage: 50 });Test deterministic behavior:
// Same user → same result (always!)evaluateFlag(flag50, { userId: 'user-1', environment: 'production' });evaluateFlag(flag50, { userId: 'user-1', environment: 'production' });// Both results are identical ✅Rule-based targeting:
const ruleFlag = createFeatureFlag('beta', { percentage: 0, // 0% for everyone rules: [{ key: 'environment', value: 'staging' }] // but 100% for staging});
evaluateFlag(ruleFlag, { userId: 'u1', environment: 'staging' });// { enabled: true } — rule match!
evaluateFlag(ruleFlag, { userId: 'u1', environment: 'production' });// { enabled: false } — no rule match, percentage is 0⚠️ Common Mistakes
Mistake 1: Using Math.random() for feature flag decisions
“Math.random() < 0.5 for 50% — easy!” → Result changes every call! User sees feature disappear/reappear. ใช้ deterministic hash
Mistake 2: Not handling 100% and 0% edge cases
“100% works like any other percentage” → 100% ต้อง always return true, 0% always return false — no hash needed
Mistake 3: Ignoring rule-based targeting
“Just use percentage, rules are too complex” → Rules สำคัญสำหรับ staging/production split และ internal user testing
Mistake 4: Not providing a reason in evaluateFlag result
“enabled true/false ก็พอ” →
reasonstring ช่วย debugging — รู้ว่า flag เปิดเพราะ rule หรือ percentage
📝 Knowledge Check
📝 Knowledge Check
Q1:ทำไม Feature Flags จึงต้องใช้ deterministic hash แทน Math.random()?
Q2:evaluateFlag ควร check อะไรก่อน — rules หรือ percentage?
Q3:Feature flag ที่มี percentage: 100 ควร return ผลลัพธ์อะไร?
🏋️ Quest: Feature Flag System
Now it’s time to practice! Build a feature flag system with deterministic rollout.
-
Download ไฟล์เริ่มต้นของ quest:
Terminal window npx bluebeltdojo download quest-114-feature-flagscd quest-114-feature-flags -
เปิด
problem.jsใน editor ของคุณ — สังเกต TODO stubs สำหรับcreateFeatureFlag()และevaluateFlag() -
Implement
createFeatureFlag(name, config):- Return flag object ที่มี
nameและconfig - ใช้ default config ถ้าไม่ได้ส่งมา
- Return flag object ที่มี
-
Implement
evaluateFlag(flag, context):- ตรวจ rules ก่อน — ถ้า rule match ให้ return true
- ตรวจ percentage — ใช้ deterministic hash ของ userId (ไม่ใช่ Math.random!)
- Return
{ enabled: boolean, reason: string }
-
สำคัญ: ทดสอบว่า userId เดียวกันได้ผลเดิมเสมอ (deterministic)
-
ตรวจสอบ solution ของคุณ:
Terminal window node test.js -
When all tests pass, submit your solution:
Terminal window npx bluebeltdojo submit
💡 Tip: ถ้า “deterministic: same user gets same result” ล้มเหลว — ตรวจสอบว่าคุณใช้ hash ของ userId ไม่ใช่ Math.random()
คำใบ้
- อ่าน instructions ใน
problem.jsอย่างละเอียด - สำคัญมาก: ใช้ deterministic hash สำหรับ user assignment — ห้ามใช้ Math.random()!
- 100% rollout ต้อง always enable, 0% rollout ต้อง always disable
- Rules ควร check ก่อน percentage
- ถ้าติดขัด ลองอ่าน “Common Mistakes” อีกครั้ง — อย่าดู solution โดยตรง