Feature Toggles, Remote Config, and A/B Testing in Android: Ship Faster Without Fear
Imagine you’re an app developer. You’ve worked hard on a new feature, but just before it’s ready, a bug appears. You have a hard choice: either you delay the launch to fix the bug, or you release the feature with the bug and hope it doesn’t cause problems.
This happens because usually, when you release new code, the new features are turned on right away. A better way to work is a method called progressive delivery. This lets you release your code and your features at different times. This means you can ship code all the time while having full control over when and how new features are turned on.
To do this, you can use three important tools: Feature Toggles, Remote Config, and A/B Testing. By understanding and using them, you can build a more reliable release process that uses real user data.
1. The Foundation: Feature Toggles
A Feature Toggle is like a switch in your app’s code. You can use it to turn a new feature on or off. You can build all the code for a new feature, but the switch lets you decide when people can actually see and use it. This way, you can put the new code into your app, but keep the feature hidden until you are ready.
Without a toggle:
startNewCheckoutFlow()
With a toggle:
if (isFeatureEnabled(FeatureToggle.NewCheckout)) {
startNewCheckoutFlow()
} else {
startLegacyCheckoutFlow()
}
Key Benefits:
- Safe Testing: You can hide new, experimental features behind a toggle and only show them to a small group of internal testers.
- Less Risk: If a new feature causes a big crash or problem, you can turn it off instantly with a toggle. This saves you from having to release a whole new app update just to fix the issue.
- Continuous Delivery: Developers can add new code to the main app often, knowing that the new feature will stay hidden until it’s ready for everyone.
2. The Brains: Remote Config
While a simple on/off switch in your code is good, it’s not very flexible. You can’t change it without releasing a new app update. **Remote Config** is a tool that lets you control these switches from a central place on the internet. This means you can turn features on or off, or change things like a welcome message, without making users update their app.
Remote Config allows you to change more than just on/off switches:
- Boolean flags: Turn features on or off (`enable_new_ui`).
- Strings: Update UI text or messages (`home_title`).
- Numbers: Change a setting for your app (`max_cart_items`).
- JSON objects: Store more complex information for special settings.
The most popular tool for this on Android is Firebase Remote Config, which is a powerful and simple platform with features like rolling out to a percentage of users, targeting specific groups, and A/B testing.
How it Works: The Progressive Delivery Flow
This diagram shows how your Android app, the remote configuration, and the user all connect:

Firebase Remote config
The app gets its settings from the server, which you manage using the Firebase Console. The app then uses these settings to decide what features to show the user.
3. The Experimentation Engine: A/B Testing
A/B Testing is a way to find out which version of a feature works best. You can show one version to some users (Group A) and a different version to other users (Group B). Then you compare the results to see which one is more successful, like which one gets more clicks or sales.
Remote Config gives you the ability to show different versions of a feature to different users. A/B testing is the process of setting up and looking at the results of this test. This process removes guesswork and helps you check your ideas with real user data.
The A/B Testing Process:
- Have an Idea: What do you want to test? For example, “A green checkout button will get 10% more purchases than a blue one.”
- Create Versions: Use Remote Config to make a “Control” group (Version A) with the blue button and an “Experiment” group (Version B) with the green button.
- Show to Users: The A/B testing tool randomly puts users into one of the groups.
- Track Results: Use a tool like Firebase Analytics to track your goal (e.g., button clicks) for each group.
- Analyze: The tool will tell you which version was the winner based on the data.
- Rollout: You can then turn on the winning version for all users with a single click.
Getting Started with Firebase in Android (Step-by-Step)
Here’s a simple guide on how to get started using Kotlin and Firebase.
Step 1: Add the Firebase SDK
First, you need to add the Firebase SDK to your app’s `build.gradle.kts` file.
// build.gradle.kts (Module: app)
dependencies {
// ... other dependencies
implementation("com.google.firebase:firebase-config-ktx")
implementation(platform("com.google.firebase:firebase-bom:32.7.0")) // Recommended for versioning
}
Step 2: Set Default Values
You should always set a default value for your features. These are used when the app first launches and hasn’t yet connected to the internet to get new settings, or if there’s no internet connection at all.
Create a new XML file at `res/xml/remote_config_defaults.xml`.
<!-- res/xml/remote_config_defaults.xml -->
<defaultsMap>
<entry>
<key>enable_new_ui</key>
<value>false</value>
</entry>
<entry>
<key>welcome_message</key>
<value>"Hello from defaults!"</value>
</entry>
</defaultsMap>
Step 3: Get & Activate the Config
In your app’s main code, get the latest settings from the server.
// A good place to do this is when your app first starts.
val remoteConfig = Firebase.remoteConfig
// Set how often the app should check for new settings (e.g., 1 hour)
val configSettings = remoteConfigSettings {
minimumFetchIntervalInSeconds = if (BuildConfig.DEBUG) 0 else 3600
}
remoteConfig.setConfigSettingsAsync(configSettings)
// Set the default values from the XML file
remoteConfig.setDefaultsAsync(R.xml.remote_config_defaults)
// Get the latest settings from the server and use them
remoteConfig.fetchAndActivate()
.addOnCompleteListener { task ->
if (task.isSuccessful) {
val updated = task.result
Log.d("RemoteConfig", "Config params updated: $updated")
} else {
Log.e("RemoteConfig", "Fetch failed.")
}
// Now you can use the settings, whether they are new or the defaults.
updateUI()
}
Step 4: Read Flags in Code
You can read the flags anywhere in your code after they have been loaded.
fun updateUI() {
val remoteConfig = Firebase.remoteConfig
val isEnabled = remoteConfig.getBoolean("enable_new_ui")
val welcomeMessage = remoteConfig.getString("welcome_message")
// Use the values to control what the user sees
if (isEnabled) {
// Show the new user interface
} else {
// Show the old user interface
}
val textView = findViewById<TextView>(R.id.welcome_text_view)
textView.text = welcomeMessage
}
Advanced Strategies & Best Practices
Structuring Feature Flags (Clean & Scalable)
For bigger apps, using a helper function with a list of all your flags is a good way to keep your code clean and organized.
enum class FeatureToggle(val key: String) {
NewDashboard("feature_new_dashboard"),
CheckoutV2("feature_checkout_v2"),
DynamicBanner("feature_dynamic_banner")
}
fun isFeatureEnabled(toggle: FeatureToggle): Boolean {
return Firebase.remoteConfig.getBoolean(toggle.key)
}
This prevents you from having to type the same flag names over and over and helps avoid mistakes.
Scalable Rollout Strategy
A smart rollout plan lowers risk and lets you test new features with real users before you launch them to everyone.
Stage | Description |
---|---|
Dev Only | The feature is only available to your internal development and test accounts. |
Internal QA | The feature is turned on for a small group of internal testers. |
5% Rollout | You release the feature to a small percentage of real users to get early feedback and check for problems. |
50% Rollout | You slowly give the feature to more users to make sure it’s stable and works well. |
100% Rollout | The feature is stable and is turned on for all users. |
Cleanup | After the feature is fully released and stable, you remove the switch and the old code. |
Analytics & Monitoring
To make decisions based on facts, you must link your feature flags to your crash reports and analytics. Firebase Crashlytics and Analytics make this easy.
// Tell Crashlytics which features are on for the user
Crashlytics.setCustomKey("feature_new_dashboard", isFeatureEnabled(FeatureToggle.NewDashboard))
// Track which version of a test a user is in
Firebase.analytics.setUserProperty("checkout_variant", variant)
Conclusion
Using Feature Toggles, Remote Config, and A/B Testing changes the way you build apps for the better. Instead of big, risky releases, you can constantly improve your app based on what you learn. You can try new ideas with a small group of users, turn off bad features right away, and make the app better for everyone.
By shipping faster without fear, you can stay ahead of the competition and build a product that your users truly love.