How to Implement Feature Flags and Gain More Control over Your App?

How to Implement Feature Flags and Gain More Control over Your App?

Every person with software development experience can share a few stories about incorrectly working features. We build our apps out of small pieces, introduce advanced architecture patterns, and yet sometimes one of the elements fails, causing bugs or even system failures. In such cases, feature toggling can save the day! Find out how to implement feature flags, and make your app more stable.

What is feature toggling?

Simply saying, feature toggles (sometimes also called feature flags) are on/off switches that allow us to dynamically enable and disable some parts of a system. Even during its runtime.

The idea is pretty straightforward – our app or system communicates with another service that decides, if a given feature should be enabled, or not.

And then what? For example, depending on this factor our app can show the user some set of buttons or hide it away from him. It can also display a popup with information that this function is currently unavailable.

Benefits of feature toggling

Feature toggles are a field-tested solution that proves useful in multiple situations and is widely adopted in software development. But why should we spend time on implementing and supporting it? Here’s a short list of reasons:

1. More control over our own software
When the system fails, a problematic element can be disabled, but with feature toggle the rest can still function. This ensures a better user experience than the classic approach when the whole system is locked out and the user can only see the Something went wrong screen.

2. Simplified integration of deployment process between back-end and front-end services
Client apps can be released without the need for the back-end to be ahead of them. The features not yet supported by the back-end can be hidden behind the feature toggles and disabled until they’re implemented on both sides.

3. Easier to maintain release cycle
This depends on the release plan for our software, but we usually encounter issues concerning unfinished or incorrectly working features that make sticking to the schedule really difficult. Thanks to feature toggles, when this happens, we can still release our application on time and simply keep the unfinished parts disabled until they’re ready.

4. Releases for groups of users
Specific features can be enabled only for fractions or groups of users, so we could analyze how a given feature impacts the business and how it’s perceived by users. This can provide a lot of valuable info and simplify A/B testing, helping our application to grow.

These arguments should be enough to convince most people that feature toggles are something that everybody can benefit from – end users, business, and software developers.

Defining feature flag values – best practices

Implementation of feature flags in mobile applications can be really easy if we know what tools to use, and what principles we should follow during the setup.

These are some of the most important rules for defining feature toggles:

  • Simplicity – the toggle should be no more than an on/off switch that describes a single feature, or a connected group of features. Introducing more complexity may lead to unnecessary confusion as the number of feature toggles grows.
  • Refreshing and caching data – we often want to remember the last state of our feature toggles and save it for offline use. But we also want feature states to refresh themselves from time to time, so our application could stay in sync with the current state of toggles.
  • Ability to override values – we should provide some kind of solution to change the state of feature toggles on-demand. This makes development and testing much easier. Trust us, the QA team will be really thankful!

Taking all of the arguments above into consideration, we can start setting up a feature flag in our application.

How to implement feature flags?

Source of feature flags

First of all, we need to define where our feature toggle states will be stored. The most natural choice seems to be Firebase Remote Config since most mobile apps use Firebase services anyway. But it’s not the only solution – any key-value store will do just fine. If we want, we can even use REST API and retrieve toggles from our own server.

The remote config approach, however, has some nice out-of-the-box benefits:

  1. Official native SDKs for Android, iOS, and Flutter.
  2. Ability to store a file (XML on Android; Plist on iOS) with default values, that can be used if the application is unable to fetch data from the server.
  3. Setting different values depending on specified conditions, which comes in super handy, for example, when we want to enable one feature for Android but disable it for iOS.

For detailed setup instructions, it’s probably best to follow dedicated Android and iOS guides.

Adding parameters in remote config

Firebase web UI provides a set of controls, allowing us to define key and description for each value.

Default values can contain any valid JSON type data, but in our case, a simple boolean value will be enough. After adding a parameter, we can publish the changes, and our remote config will be instantly available for client services.

Setting conditions for feature flags

When adding a parameter, Firebase allows us to set different values depending on specified conditions.

Condition sets can be built on top of different analytics or groups provided by Firebase Analytics. But they can also be extended by specifying custom values in Firebase.

For example, we can set a condition value specifically for Android devices:

Feature flags: Defining a new condition for parameters

Similarly, we can make the same type of condition for iOS devices. This allows us to enable given feature on only one platform:

Adding a parameter key only for Android platform

Using feature flags on Android

When our instance of Firebase Remote Config is ready, we can start implementing support for it in Android and iOS apps. In this article, we use Android and Kotlin as examples, but the whole procedure is almost the same for iOS and Swift.

Fetching values of feature flags

Before we start using feature flags, we need to fetch the Remote Config. This operation will retrieve the latest values from Firebase. Usually, it should be conducted just once, when the application starts. But we also recommend checking out Firebase Remote Config Loading Strategies to see other possible approaches. The entire process is well documented in Android and iOS guides, so we’ve decided to skip it here.

Defining the feature data model

For each feature, we have to define a “key” field that will match the key stored in Remote Config. In addition, it may be a good idea to add fetchImmediately. It’s a boolean value that determines whether Remote Config should be refreshed before checking feature value or not.

enum class Feature(
	val key: String,
	val fetchImmediately: Boolean = false
) {
    	key = "feature_flag_test",
    	fetchImmediately = false
    	key = "feature_depending_on_platform",
    	fetchImmediately = true

Implementing feature flag checks

The interface is so simple, it takes only one method to check our feature flag:

interface FeatureManager {
	suspend fun isEnabled(feature: Feature): Boolean

Proper implementation of the interface above must rely on Remote Config data and respect our fetchImmediately parameter.

The example below should be suitable in most cases, but if needed, we can extend it with additional features.

class RemoteFeatureManager : FeatureManager {
	// Instance of remote config, probably best to inject it using DI
	// In order to make testing easier
	private val remoteConfig = Firebase.remoteConfig.apply {
    	// Attaching default values from XML file
    	// In this block additional parameters can be configured
    	setConfigSettingsAsync(remoteConfigSettings {
        	minimumFetchIntervalInSeconds = 300

	override suspend fun isEnabled(feature: Feature): Boolean {
    	// Refreshing remote config depending on fetchImmediately value
    	// If needed, additional check can be added to check if config fetch was successful
    	if (feature.fetchImmediately) remoteConfig.fetchAndActivate().await()
    	// Returning remote config value assigned to feature key
    	return remoteConfig.getBoolean(feature.key)

For debugging and quality assurance purposes, we can use either separate Firebase instances with different Remote Config values, or a local override.

The latter can be easily implemented with SharedPreferences. All we need to do is to set the features as enabled by default and add one more method for changing feature toggle states. Later, it can be available from our debug screen or QA plugin (we recommend Hyperion), so we could quickly switch values if needed.

class LocalFeatureManager(context: Context) : FeatureManager {
	companion object {
  	const val FEATURE_PREFERENCES_NAME = "feature_flags"
	// Instance of shared preferences instead of remote service
	private val sharedPreferences = context.getSharedPreferences(

	// Value is read from SharedPreferences, and enabled by default
	override suspend fun isEnabled(feature: Feature): Boolean {
    	return sharedPreferences.getBoolean(feature.key, true)
	// Can be used to turn features on and off from
    fun setFeatureState(feature: Feature, isEnabled: Boolean) {
        	.putBoolean(feature.key, isEnabled)

Since these implementations are built on top of a common interface, we can also add an option that allows switching between them, somewhere in our app. This way the QA team can use both remote and local feature flag sets on-demand.


Integrate feature flags and allow the development teams to deliver a more stable, modular system with an additional fallback mechanism we can use when something goes wrong. Sure, they make the code more complex and require some additional checks here and there, but their return value is often very high. They are extremely useful also in the continuous development process, as our system grows and the number of new features increases. Give them a shot!



Igor Kurek

Software Developer at Holdapp, with over 4 years of experience in Android development. Igor enjoys creating and breaking different types of software, both at work and after hours. When he’s not looking at the screen, he enjoys all kinds of music-related stuff and modern literature.

Learn more

Android Small Talks: Backendless – an Alternative to was one of the most popular MBaaS services (Mobile backend as a service). It facilitated creating a database in a cloud and made access to it available through an automatically generated API. Unfortunately, some time ago this project was announced to be closed, which forced us to find an alternative. From many websites of this sort, I have finally decided to choose

Read more

Android Small Talks: Dependency Injection with Dagger 2

Dependency injection is a design pattern the main task of which is to free our code of dependencies. As we all surely realize, code with a minimal amount of dependencies is far easier to manage and change. It’s also easier to use and test such code.

Read more

Android Small Talks: MemoryLeaks in Android

A memory leak occurs when you do not release memory allocated before, and you no longer need objects for which this memory has been previously reserved. This involves losing control over a certain area of memory until the end of the process life cycle. In some languages, we have to personally deal with releasing memory. In C we need to use the free instruction, in C++ the delete statement. And what about Java?

Read more

Project estimation

Let us know what product you want to build and how we can help you.

Why choose us?

Logo Mobile Trends Awards

Mobile Trends Awards 2021

Winning app in

Nagroda Legalnych Bukmacherów

Legal Bookmakers Award 2019

Best Mobile App

Mobile Trends Awards logo

Mobile Trends Awards 2023



client reviews

Clutch logo