
Feature Flags
- 18 installs
- 60 repo stars
- Updated June 14, 2026
- ahmed3elshaer/everything-claude-code-mobile
feature-flags is a Claude Code skill providing mobile feature-flag patterns for LaunchDarkly and Firebase Remote Config with A/B testing and gradual rollouts.
About
feature-flags is a Claude Code skill with feature-flag patterns for mobile apps. A developer uses it to wire up a type-safe flag provider backed by Firebase Remote Config or LaunchDarkly, define flags once, and drive A/B tests or gradual rollouts. It shows Android and iOS implementations plus shared KMP flag evaluation.
- Type-safe FeatureFlagProvider interface with sealed flag definitions
- Firebase Remote Config and LaunchDarkly provider implementations for Android and iOS
- Covers A/B testing, gradual rollouts, and KMP shared flag evaluation
Feature Flags by the numbers
- 18 all-time installs (skills.sh)
- Ranked #776 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
feature-flags capabilities & compatibility
- Capabilities
- feature flags · ab testing · remote config · gradual rollout
- Use cases
- api development
What feature-flags says it does
Feature flag patterns - LaunchDarkly, Firebase Remote Config, local feature flags, A/B testing, gradual rollouts, and KMP shared flag evaluation.
npx skills add https://github.com/ahmed3elshaer/everything-claude-code-mobile --skill feature-flagsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 18 |
|---|---|
| repo stars | ★ 60 |
| Last updated | June 14, 2026 |
| Repository | ahmed3elshaer/everything-claude-code-mobile ↗ |
What it does
Integrate a feature-flag provider (Firebase Remote Config or LaunchDarkly) into a mobile app for rollouts and A/B tests.
Who is it for?
Mobile developers adding feature flags, A/B tests, or gradual rollouts
When should I use this skill?
You are integrating LaunchDarkly or Firebase Remote Config into a mobile app
By the numbers
- Covers 2 remote providers (Firebase Remote Config, LaunchDarkly) plus local flags
- Type-safe sealed flag definitions
Files
Feature Flag Patterns
Architecture
FeatureFlagProvider Interface (Kotlin)
interface FeatureFlagProvider {
fun getBooleanFlag(key: String, default: Boolean = false): Boolean
fun getStringFlag(key: String, default: String = ""): String
fun getIntFlag(key: String, default: Int = 0): Int
fun getDoubleFlag(key: String, default: Double = 0.0): Double
suspend fun refresh()
}Type-Safe Flag Definitions
sealed class FeatureFlag<T>(
val key: String,
val defaultValue: T
) {
// Boolean flags
object NewOnboarding : FeatureFlag<Boolean>("new_onboarding_v2", false)
object DarkModeEnabled : FeatureFlag<Boolean>("dark_mode_enabled", true)
object ChatFeature : FeatureFlag<Boolean>("chat_feature", false)
// String flags
object CheckoutButtonText : FeatureFlag<String>("checkout_button_text", "Buy Now")
object HomeLayoutVariant : FeatureFlag<String>("home_layout_variant", "control")
// Numeric flags
object MaxCartItems : FeatureFlag<Int>("max_cart_items", 50)
object SearchDebounceMs : FeatureFlag<Long>("search_debounce_ms", 300L)
}
// Type-safe evaluation
class FeatureFlagManager(private val provider: FeatureFlagProvider) {
fun isEnabled(flag: FeatureFlag<Boolean>): Boolean {
return provider.getBooleanFlag(flag.key, flag.defaultValue)
}
fun getString(flag: FeatureFlag<String>): String {
return provider.getStringFlag(flag.key, flag.defaultValue)
}
fun getInt(flag: FeatureFlag<Int>): Int {
return provider.getIntFlag(flag.key, flag.defaultValue)
}
}---
Firebase Remote Config
Setup and Initialization
class FirebaseFeatureFlagProvider(context: Context) : FeatureFlagProvider {
private val remoteConfig = Firebase.remoteConfig.apply {
val configSettings = remoteConfigSettings {
minimumFetchIntervalInSeconds = if (BuildConfig.DEBUG) 0 else 3600
}
setConfigSettingsAsync(configSettings)
setDefaultsAsync(R.xml.remote_config_defaults)
}
override fun getBooleanFlag(key: String, default: Boolean): Boolean {
return remoteConfig.getBoolean(key)
}
override fun getStringFlag(key: String, default: String): String {
return remoteConfig.getString(key).ifEmpty { default }
}
override fun getIntFlag(key: String, default: Int): Int {
return remoteConfig.getLong(key).toInt()
}
override fun getDoubleFlag(key: String, default: Double): Double {
return remoteConfig.getDouble(key)
}
override suspend fun refresh() {
remoteConfig.fetchAndActivate().await()
}
}Default Values (res/xml/remote_config_defaults.xml)
<?xml version="1.0" encoding="utf-8"?>
<defaultsMap>
<entry>
<key>new_onboarding_v2</key>
<value>false</value>
</entry>
<entry>
<key>dark_mode_enabled</key>
<value>true</value>
</entry>
<entry>
<key>checkout_button_text</key>
<value>Buy Now</value>
</entry>
</defaultsMap>iOS Firebase Remote Config
final class FirebaseFeatureFlagProvider: FeatureFlagProvider {
private let remoteConfig = RemoteConfig.remoteConfig()
init() {
let settings = RemoteConfigSettings()
#if DEBUG
settings.minimumFetchInterval = 0
#else
settings.minimumFetchInterval = 3600
#endif
remoteConfig.configSettings = settings
remoteConfig.setDefaults(fromPlist: "RemoteConfigDefaults")
}
func getBooleanFlag(key: String, defaultValue: Bool) -> Bool {
remoteConfig.configValue(forKey: key).boolValue
}
func getStringFlag(key: String, defaultValue: String) -> String {
let value = remoteConfig.configValue(forKey: key).stringValue
return value?.isEmpty == false ? value! : defaultValue
}
func refresh() async throws {
let status = try await remoteConfig.fetchAndActivate()
print("Remote config fetch status: \(status)")
}
}---
LaunchDarkly
Android SDK Initialization
class LaunchDarklyProvider(context: Context) : FeatureFlagProvider {
private val client: LDClient
init {
val ldConfig = LDConfig.Builder(LDConfig.Builder.AutoEnvAttributes.Enabled)
.mobileKey("mob-your-mobile-key")
.build()
val ldContext = LDContext.builder(ContextKind.DEFAULT, "user-id-123")
.set("email", "user@example.com")
.set("plan", "premium")
.build()
client = LDClient.init(context.applicationContext, ldConfig, ldContext, 5)
}
override fun getBooleanFlag(key: String, default: Boolean): Boolean {
return client.boolVariation(key, default)
}
override fun getStringFlag(key: String, default: String): String {
return client.stringVariation(key, default)
}
override fun getIntFlag(key: String, default: Int): Int {
return client.intVariation(key, default)
}
override fun getDoubleFlag(key: String, default: Double): Double {
return client.doubleVariation(key, default)
}
override suspend fun refresh() {
// LaunchDarkly uses streaming by default, manual refresh not needed
}
fun registerFlagChangeListener(key: String, listener: (Boolean) -> Unit) {
client.registerFeatureFlagListener(key) { flagKey ->
listener(client.boolVariation(flagKey, false))
}
}
}---
Local Feature Flags
BuildConfig-Based Flags
// build.gradle.kts
android {
buildTypes {
debug {
buildConfigField("boolean", "ENABLE_DEV_TOOLS", "true")
buildConfigField("boolean", "MOCK_API", "true")
}
release {
buildConfigField("boolean", "ENABLE_DEV_TOOLS", "false")
buildConfigField("boolean", "MOCK_API", "false")
}
}
}
// Usage
if (BuildConfig.ENABLE_DEV_TOOLS) {
showDevMenu()
}Debug Menu Toggle
class LocalFeatureFlagProvider(
private val prefs: SharedPreferences
) : FeatureFlagProvider {
override fun getBooleanFlag(key: String, default: Boolean): Boolean {
return prefs.getBoolean("flag_$key", default)
}
fun overrideFlag(key: String, value: Boolean) {
prefs.edit().putBoolean("flag_$key", value).apply()
}
fun clearOverrides() {
prefs.edit().clear().apply()
}
}---
KMP Shared Flags
// commonMain
interface SharedFeatureFlags {
fun isEnabled(key: String, default: Boolean = false): Boolean
fun getString(key: String, default: String = ""): String
}
// androidMain
class AndroidFeatureFlags(context: Context) : SharedFeatureFlags {
private val remoteConfig = Firebase.remoteConfig
override fun isEnabled(key: String, default: Boolean) = remoteConfig.getBoolean(key)
override fun getString(key: String, default: String) = remoteConfig.getString(key)
}
// iosMain
class IosFeatureFlags : SharedFeatureFlags {
private val remoteConfig = RemoteConfig.remoteConfig()
override fun isEnabled(key: String, default: Boolean) =
remoteConfig.configValue(forKey: key).boolValue
override fun getString(key: String, default: String) =
remoteConfig.configValue(forKey: key).stringValue ?: default
}---
Gradual Rollout Pattern
class GradualRollout(private val userId: String) {
fun isInRollout(flagKey: String, percentage: Int): Boolean {
val hash = "$flagKey-$userId".hashCode().absoluteValue
return (hash % 100) < percentage
}
}
// Server-side targeting in Firebase Remote Config:
// Condition: "10% of users" -> random percentile <= 10
// Condition: "Premium users" -> user property "tier" == "premium"A/B Testing: Variant Assignment
data class Experiment(
val name: String,
val variant: String // "control", "variant_a", "variant_b"
)
fun getExperimentVariant(flagManager: FeatureFlagManager): Experiment {
val variant = flagManager.getString(FeatureFlag.HomeLayoutVariant)
analytics.logEvent("experiment_assigned", mapOf(
"experiment_name" to "home_layout",
"variant" to variant
))
return Experiment("home_layout", variant)
}Related skills
FAQ
Which providers are covered?
Firebase Remote Config and LaunchDarkly, plus local feature flags, with a shared FeatureFlagProvider interface.
Does it support A/B testing?
Yes. It covers A/B testing and gradual rollouts, including string flags for layout variants.