Feature Toggles Implementation for Mobile App Functionality Management
Feature toggle—mechanism to enable/disable functionality without a new app release. Sounds simple until you think about client versions, flag caching, graceful degradation when server is unavailable, and accumulated dead flags after a year.
Why Flags Matter in Mobile Apps
Trunk-based development. Developer merges incomplete feature under a flag—CI/CD works normally, QA doesn't see raw code, production has it disabled. Long-lived feature branches with merge conflicts after three weeks—history.
Gradual rollout. New feature enables first for 1% of users → 10% → 50% → 100%. If Crashlytics shows crash spike—disable flag instantly, no release recall.
A/B tests. One button green, another blue. Flag with variants, analytics show which converts better.
Kill switch. Payment provider down—disable payment screen, show placeholder. No hotfix, no App Store review.
Implementation Options
Firebase Remote Config—de facto standard for most mobile apps. Free, SDK for iOS/Android/Flutter/React Native, personalization by user properties (country, app version, segment). Downside: doesn't suit enterprise with strict data storage requirements.
LaunchDarkly—enterprise solution with targeting rules, A/B tests, audit trails, SSO. Expensive, but justified with 50+ developers and hundreds of flags.
Custom service—when full control needed or can't send user data to third-party servers. Go/Node/Laravel backend, PostgreSQL for flags, Redis for cache, WebSocket or polling for real-time updates.
Unleash—open source LaunchDarkly alternative, self-hosted. SDK for all platforms, targeting, gradual rollout. Good balance between functionality and control.
Technical Client Implementation
Key rule: flags work offline. First run—default values from bundle, subsequent runs—cached from UserDefaults/SharedPreferences, background update from server.
iOS (Swift):
// FeatureFlagService with caching
actor FeatureFlagService {
private var flags: [String: Bool] = [:]
func isEnabled(_ flag: FeatureFlag) -> Bool {
flags[flag.rawValue] ?? flag.defaultValue
}
func refresh() async {
// Load from Remote Config or custom API
let fetched = await remoteConfigService.fetch()
flags = fetched
}
}
// Usage in SwiftUI
if featureFlagService.isEnabled(.newCheckoutFlow) {
NewCheckoutView()
} else {
LegacyCheckoutView()
}
Android (Kotlin): similarly via StateFlow in ViewModel so UI reacts to flag changes in real-time without screen restart.
Flag Lifecycle Management
Main operational problem: flags accumulate. After a year: if (featureFlags.isEnabled("new_onboarding_v2_2023"))—dead flag nobody disables because unclear what risks it. Code unreadable, tests don't cover both branches.
Process: each flag created with planned deletion date (expires: 2024-06-01). After full rollout—task to remove flag and unneeded code branch. Tool: ArchUnit (Android) or static analysis (SwiftLint custom rule) to detect expired flags in CI.
Timeline: Firebase Remote Config integration + basic flags—2–3 days. Custom feature-flag service with targeting, rollout, dashboard—3–4 weeks.







