◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 28 days left. If your app calls it, check the migration target●SDK 58 ISSUE — Early reports include Android widgets not refreshing on a schedule and Stripe linking statically●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 28 days left. If your app calls it, check the migration target●SDK 58 ISSUE — Early reports include Android widgets not refreshing on a schedule and Stripe linking statically●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/Dev Tools
⬡ Dev Tools/2026-06-13Advanced

Before Your Feature Flags Turn Into a Junkyard — Governing Naming, Staged Rollout, and Kill Switches Across Six Apps

Keep adding remote-config flags and within six months you have keys nobody remembers the meaning of. Here is a governance system — naming conventions, safe defaults, staged rollout, kill switches, and monthly cleanup — with the implementation from running six apps in parallel.

feature flagsremote configoperations10Firebase10governance

✦ Premium Article

I remember the day I added the first flag to remote config. It was a key called new_onboarding for showing a new onboarding screen to a slice of users. It was convenient. So convenient that six months later nearly thirty flags sat there, including ones like test2 and enable_v3_real whose meaning nobody could explain anymore.

Feature flags themselves are powerful. The problem is an asymmetry: adding one is easy, removing one is tedious. When the same thing happens across six apps at once, the config screen quickly turns into a wasteland. Here is the governance system I settled on to avoid that, along with the actual naming convention and the rollout and kill-switch implementation.

A naming convention your future self can read

The first thing I decided was the naming convention. A flag name should tell you which app, what it controls, and how long it is meant to live.

<scope>.<domain>.<name>.<kind>

examples:
  wallpaper.onboarding.skip_tutorial.release   # all apps, permanent
  fortune.paywall.intro_offer_v2.experiment    # fortune apps only, experiment
  all.admob.interstitial_enabled.ops           # all apps, operational switch

The trailing kind is the crux. It sorts flags into three lifespans. A release flag is temporary, used to roll a new feature out in stages; an experiment flag is for A/B tests and dies when the test ends; an ops flag is a permanent operational switch like a kill switch. With this classification, the monthly cleanup below becomes a mechanical "is this safe to delete?" decision.

I made scope able to target a group of apps because settings I wanted to apply only to the three fortune apps came up constantly. all means every app; anything else filters by an app group ID.

Defaults must fail to the safe side

The biggest trap in remote config is behavior on a failed fetch. Right after launch or while offline, the flag value has not arrived yet. If you treat "value not fetched = feature ON," an unverified feature leaks to every user.

As a rule, when a flag cannot be fetched, behavior falls back to the previous safe state. For a new-feature flag the default is OFF; for an operational kill switch the default is ON (do not stop).

import remoteConfig from '@react-native-firebase/remote-config';
 
// State the fallback per flag explicitly
const DEFAULTS = {
  'wallpaper.onboarding.skip_tutorial.release': false, // new feature -> default OFF
  'all.admob.interstitial_enabled.ops': true,          // ops -> default do-not-stop
} as const;
 
export async function initFlags(): Promise<void> {
  await remoteConfig().setDefaults(DEFAULTS);
  await remoteConfig().setConfigSettings({
    minimumFetchIntervalMillis: 60 * 60 * 1000, // 1-hour cache normally
  });
  // DEFAULTS still apply on failure, so do not swallow it — just record it
  try {
    await remoteConfig().fetchAndActivate();
  } catch (e) {
    logFlagFetchError(e);
  }
}
 
export function flag(key: keyof typeof DEFAULTS): boolean {
  return remoteConfig().getValue(key).asBoolean();
}

Declaring every flag's safe value in setDefaults is the point. With it, even a device that has never reached the server gets defined, safe behavior. I treat this list of defaults as the single source of truth in code, and I keep a strict order: add the default here first, then create the flag on the server side.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦A naming convention you can still read six months later, plus a rule that sorts flags into three lifespans
✦A default design that always fails to the safe side, and a staged rollout you can run in 5% increments
✦A kill switch that stops a feature in production within 30 seconds, and a monthly routine to retire dead flags
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⬡ Dev Tools2026-05-19
Rolling Out Firebase App Check in Rork Without Breaking AdMob or Crashlytics
A practical, staged rollout for Firebase App Check in Rork apps that keeps Crashlytics reporting, Realtime Database listeners, and AdMob payouts intact — written from running AdMob-funded indie apps in production.
⬡ Dev Tools2026-05-16
Migrating Firebase CocoaPods to SPM in Rork Max Apps
Firebase stops publishing new Apple SDK versions to CocoaPods in October 2026 (Firebase 12 is the last major release). Here's a detailed migration log from moving 4 Rork Max iOS apps to Swift Package Manager — including dSYM failures, module errors, and the Dropbox conflict copy problem you'll definitely hit.
⬡ Dev Tools2026-03-22
Making Feature Flags Survive Production: Kill Switches and Gradual Rollouts in Rork Max
Wire Firebase Remote Config into a Rork Max app, then harden it: kill switches that work offline, stable bucket assignment for gradual rollouts, and a registry that keeps flags from turning into debt.