Firebase Remote Config: Type-Safe Feature Flags and Scalability
As markets move fast, so should app development. MVP development within an Agile framework relies on shipping features on a weekly basis, on average. The speed of feature development & deployment is directly tied to the early-stage startups’ ability to iterate their way to product-market fit. And, 7 in 10 companies recognize that they fail to roll out updates quickly enough, according to the Atlassian survey. This is where Firebase Remote Config comes in with its ability to maintain that speed and flexibility, without the overhead of republishing app update releases excessively.
Firebase Remote Config is a cloud service provided by Google Firebase. In short, it enables flexible product decisions such as turning features on/off, A/B testing, and selective feature rollout. It is all done without the need for a user to download updates from the Play Store. This is said assuming the logic is already in the code and you want to either toggle it, serve different key-value pairs, or make it available to certain user categories only.
When your project grows and becomes modular, Firebase Remote Config:
- can lead to repetitive code,
- has a lack of type safety, and
- string-based config keys can cause silent failures.
Shortly, Google’s Firebase Remote Config is a great tool… until it isn’t. So, to make great tools work in more complex cases, we, at You are launched, tend to find smarter solutions. Here, we’ll introduce a solution for Firebase Remote Config—a lightweight abstraction that enforces type safety, eases feature parsing, and makes it work for complex, modular, fast-moving projects.

Table of contents
The Business Problem Behind the Coding Solution
When a completely new feature logic is added to the app, the team makes peace with the app resubmission and review process. Then, users painstakingly go through the inconvenience of having to update the app. Not to mention, the blackout dates for the mobile app stores during the holidays. However, there is absolutely no need to go through that if you need to change the values for the existing functionality. For instance, when marketing and product teams begin their testing of user flows, pricing strategies, pushing different banners, in-app campaigns, and tweaking UI elements. As such, feature flags emerged as a well-established industry standard to enable nearly continuous development.
In fact, it was back in 2021 when Atlassian did a survey for feature flag adoption in the industry. Here are some highlights:
- Almost all companies adopted feature flags – 95%;
- All companies noticed the benefits: reduced risk, increased speed of development & deployment.

And while the positive effects are solid and substantial, the same survey reports the challenge:
- Safe implementation and rollout of new functionality is a common concern for two thirds of the companies.

The Codebase Issues with Firebase Remote Config
So, Firebase Remote Config works with string-based feature flags. For instance, to enable a feature, there will be a string something like “new_feature_enabled”. Imagine a typo in that – “new_feature_enable” – simply missing out a ‘d’. This is not a typo in terms of spelling, so the developer’s environment (IDE) is not likely to pick it up or flag it. And when the product team tries to measure the impact of this new feature “turned on” with Firebase Remote Config and nothing is happening, everyone understands it is a bug. If there are a few features and the project is relatively small, finding it fast is quite doable. However, tracing this typo in a codebase with hundreds of nested directories is a developer’s rabbithole.
Many reddits, memes, and emotional outcries are dedicated to this, along with many work days lost, product development delays, and cross-team heated arguments. This pain is not simply about Firebase Remote Config, but in general for any bit of code that ends up using a magic string. This leads to real business problems such as waste of developers’ hours and losing speed for marketing/product development, along with causing cross-team communication issues and a chain of failing deadlines. One of the engineering solutions to avoid all that is abstractions utilizing enums, which are just a few lines of boilerplate.


This leads us to summarize the limitations of using Firebase Remote Config as is, relying on its out-of-the-box implementation.
Out-of-the-box Firebase Remote Config: The Hidden Business Costs
Let’s be honest, development today relies on AI for productivity, but it is a double-edged sword. Unless one specifically prompts to create abstracted, type-safe code, AI tools will generate basic code that works but introduces fragility to the codebase.
Another point is that when looking at Firebase Remote Config documentation, it provides solid guidance on its usage, but does not discuss issues of maintainability long-term, architectural boundaries, or handling issues arising from growing developer teams. And neither do coding bootcamps or YouTube tutorials.
It often happens that a startup founder starts small and hires one or two freelancers. The stats are that 69% of developers are self-taught from online tutorials, missing out on architectural patterns. 97% of developers are using AI. In this scenario, the above-mentioned issues do not transpire as a serious problem when the codebase is small in a team of 1 or 2 developers. However, when the idea works out and starts gaining traction, leading to the expansion of teams and codebase, these issues hit the business hard. Therefore, proper Startup Services are the way to ensure lean, swift early-stage development and ensure smooth scaling.
Read our article to check whether your mobile app is ready to scale, “How to Evolve your mobile MVP App Development”
Only seasoned developers with years of expertise have seen the real costs of not having type-safety and the lack of guards for magic strings. These mistakes can play out in a quite damaging way. Let’s introduce some examples that manu companies in the industry face.
Case A: Wasted marketing budget
The Marketing team decided to do a rollout for a referral bonus. The dev team set up the referral feature to show up for all user base. However, due to a typo in one of the modules, the Firebase Remote Config defaulted to false for 30% of users. The result of the typo: dozens of hours of QA to find the bug, wasted marketing budget for several thousand dollars, extra few days to realign the effort for patching and redeploying everything, and user complaints for not getting the bonuses for the referrals.
Case B: Ruined A/B testing
The team planned A/B testing for versions A and B of the onboarding flow. A junior developer, responsible for the Firebase Remote Config, set up keys “onboarding_flow_A” and “onboarding_flow_B”. However, in reality, for variant B, the logic defaulted to false and never rolled out. Several thousand dollars spent on the design and development of flow B was not properly utilized, as it took a few weeks to realize and fix the issue.
Case C: Silent failure causing app outage
The education app used the same key in its code to launch a live quiz and to start the related animation. One expected a boolean and another a JSON object with values. A config change was made in the Firebase console to enable experiments for that feature. One module fails silently, causing a 4-hour app outage. The company loses revenues for the lost sessions during that outage, and then some as compensation to learners.
Conclusion & Solution
Abstractions that enforce type-safety, introduce error-catching, and prevent magic strings are not mere boilerplate complications. These lines of boilerplate are not simply coding improvements – they prevent business failures and make your code safely scalable. Ideally, you should prioritize this from the start. Once the business has grown and moves fast, catching those errors is time-consuming and expensive. Any QA practice would confirm that an error missed in the design costs 4-5 times more after launch, and 100 times more in the growth stage. Also, refactoring of the grown codebase with a ton of repetitive code is no easy feat as well. So, Custom Android app development services that employ seasoned developers will save you from 100x-factor losses by writing safe and scalable code.
Smarter Feature Toggles with Firebase Remote Config on Android [Tech Part]
At You are launched, we believe that good engineering is about striking the right balance between flexibility and simplicity. One place where that balance often breaks down is Remote Config — Firebase’s feature toggle and config system.
By default, Firebase Remote Config is powerful, but not always developer-friendly out of the box. So we built our own lightweight abstraction — one that simplifies reading and parsing values, encourages type safety, and plays nicely with modular architectures.
Let’s walk through how we set this up, and how we actually use it in a real Android project.
Getting Started with Firebase Remote Config
If you’re new to Firebase Remote Config, start here
👉 Get started with Firebase Remote Config
It covers everything from:
- Adding Firebase to your project
- Connecting your app to the Firebase Console
- Setting default values and parameters
- Fetching and applying config values
Once you’ve got the basics set up, come back to this article to see how we built a more scalable and developer-friendly abstraction over Remote Config.
And just a reminder, do not forget to enable Google Analytics. Firebase Remote Config is functional without but it adds finer control of the target audiences for feature rollout, experimentation, and more.

The Foundation: BaseRemoteConfigHandler
Create a base abstract class to wrap all the Remote Config logic
kotlin
abstract class BaseRemoteConfigHandler(private val isDebug: Boolean) {
protected val firebaseRemoteConfig: FirebaseRemoteConfig by lazy {
val config = Firebase.remoteConfig
if (isDebug) {
val configSettings = remoteConfigSettings {
minimumFetchIntervalInSeconds = 0
}
config.setConfigSettingsAsync(configSettings)
}
config
}
What’s going on here?
- `firebaseRemoteConfig` is the main Firebase object that gives us access to config values stored in the cloud.
- The `isDebug` flag allows us to reduce the fetch interval in development builds — so we don’t have to wait the default 12+ hours to see config changes reflected in the app.
This means: in debug builds, we fetch updates instantly; in release builds, we let Firebase use its default behavior.
Fetching Config Values
kotlin
fun fetchAndActivate(onSuccess: () -> Unit, onError: () -> Unit) {
firebaseRemoteConfig.fetchAndActivate()
.addOnCompleteListener { task ->
if (task.isSuccessful) onSuccess() else onError()
}
}
suspend fun fetchAndActivateAsync(): Boolean =
firebaseRemoteConfig.fetchAndActivate().await()
Why is this important?
Before we can read any remote values, we need to tell Firebase to fetch the latest values from the cloud and activate them.
- The `fetchAndActivate` method handles that.
- You can use it either with a callback (`onSuccess/onError`) or as a suspend function in a coroutine (`fetchAndActivate()`).
This makes it easy to fetch configs wherever your app needs them — during splash, login, onboarding, or even lazy-loaded later.
Reading Typed Config Values
kotlin
protected inline fun <reified T> read(param: RemoteConfig): T? = try {
when (T::class) {
String::class -> firebaseRemoteConfig.getString(param.key)
Boolean::class -> firebaseRemoteConfig.getBoolean(param.key)
Long::class -> firebaseRemoteConfig.getLong(param.key)
Int::class -> firebaseRemoteConfig.getLong(param.key).toInt()
Double::class -> firebaseRemoteConfig.getDouble(param.key)
Float::class -> firebaseRemoteConfig.getDouble(param.key).toFloat()
else -> {
val json = firebaseRemoteConfig.getString(param.key)
json.takeIf { it.isNotBlank() }?.let { Json.decodeFromString(it) }
}
} as? T
} catch (e: Exception) {
null
}
}
What does this do?
This `read<T>()` method is where the real magic happens.
- It reads the value from Firebase using the provided key (`param.key`) and automatically casts it to the type you need.
- It supports all common types — `String`, `Boolean`, `Int`, `Float`, `Double`, etc.
- If you store a complex object (like a JSON string), it also handles deserialization via `kotlinx.serialization`.
This removes the need to write repetitive parsing code throughout your app.
Full implementation
kotlin
abstract class BaseRemoteConfigHandler(private val isDebug: Boolean) {
protected val firebaseRemoteConfig: FirebaseRemoteConfig by lazy {
val config = Firebase.remoteConfig
if (isDebug) {
config.setConfigSettingsAsync(
remoteConfigSettings {
minimumFetchIntervalInSeconds = 0
}
)
}
config
}
fun fetchAndActivate(onSuccess: () -> Unit, onError: () -> Unit) {
firebaseRemoteConfig.fetchAndActivate()
.addOnCompleteListener { task ->
if (task.isSuccessful) onSuccess() else onError()
}
}
suspend fun fetchAndActivateAsync(): Boolean =
firebaseRemoteConfig.fetchAndActivate().await()
protected inline fun <reified T> read(param: RemoteConfig): T? = try {
when (T::class) {
String::class -> firebaseRemoteConfig.getString(param.key)
Boolean::class -> firebaseRemoteConfig.getBoolean(param.key)
Long::class -> firebaseRemoteConfig.getLong(param.key)
Int::class -> firebaseRemoteConfig.getLong(param.key).toInt()
Double::class -> firebaseRemoteConfig.getDouble(param.key)
Float::class -> firebaseRemoteConfig.getDouble(param.key).toFloat()
else -> {
val json = firebaseRemoteConfig.getString(param.key)
json.takeIf { it.isNotBlank() }?.let { Json.decodeFromString(it) }
}
} as? T
} catch (e: Exception) {
null
}
}
Defining Config Keys
kotlin
interface RemoteConfig {
val key: String
}
enum class RemoteConfigParam(override val key: String) : RemoteConfig {
TEMP_USER_GOALS_LIMIT("temp_user_goals_limit")
}
This helps us avoid using magic strings and centralizes our config keys.
Real-World Use Case: Goal Limit for Temporary Users with Firebase Remote Config
Here’s how we use Remote Config in our production app to limit features:
kotlin
object RemoteConfigHandler(isDebug: Boolean) : BaseRemoteConfigHandler(isDebug) {
fun getTempUserGoalsLimit(): Int {
return read(RemoteConfigParam.TEMP_USER_GOALS_LIMIT) ?: 3 // fallback default = 3
}
}
This object needs to be made a singleton. You can achieve this in any way you like. In this case, we use the `object` keyword.
Where to Call fetchAndActivate
You should call `fetchAndActivate()` (not suspend) early in the app lifecycle — for example, in the Splash screen, application initializer, or a background job.
A common pattern is to trigger it from the Splash screen or during initial app setup (e.g., in a `ViewModel` tied to a loading state):
kotlin
RemoteConfigHandler.fetchAndActivate(
onSuccess = { Log.d("RemoteConfig", "Updated successfully") },
onError = { Log.e("RemoteConfig", "Failed to fetch config") }
)
This does not guarantee that the latest values will be available immediately — but in most real-world scenarios, it’s good enough. Updated values will typically be applied on the next app launch or later during the session.
Tip: You usually don’t need to block for config fetch completion — just call and forget.
When to Use fetchAndActivateAsync()
Sometimes you might need to block until config values are fetched — e.g., if they control access to a critical screen or experiment logic. In such cases, we recommend this pattern:
kotlin
runCatching {
remoteConfigHandler.fetchAndActivateAsync()
}.onSuccess {
Log.d("RemoteConfig", "Updated successfully")
}.onFailure {
Log.e("RemoteConfig", "Failed to fetch config")
}
Caution: Awaiting `fetchAndActivateAsync()` may result in a multi-second delay, especially on first run or slow networks. Overusing this in UI flows can lead to bad UX (e.g., delayed screens, spinners, user frustration).
Use this approach sparingly — prefer defaults and update app behavior later where possible.
Benefits of This Approach
- Type safety: You always get the right type — or a safe fallback.
- Flexibility: Supports both primitives and complex objects (via JSON).
- Reusability: One base handler powers the whole app.
- Clean architecture: No Firebase-specific code in the business layer.
Final Thoughts
Firebase Remote Config is a powerful tool — but only if you wrap it in the right abstraction. With just a few lines of reusable boilerplate, we turned Remote Config into a clean, safe, and efficient feature toggle system.
If your app needs runtime flexibility — to A/B test features, manage user segmentation, or adjust UI/UX without redeploying — this pattern is worth adopting.
Let us know if you want to see a full implementation on GitHub or a Kotlin Multiplatform version of this!
FAQ: Firebase Remote Config: Type-Safe Feature Flags and Scalability
Firebase Remote Config is a cloud service that lets you change app behavior and appearance without requiring users to download an update.
Yes, if misconfigured, it can waste marketing spend, ruin A/B testing, or even cause outages during campaigns.
Feature flags allow faster experimentation, safer rollouts, and nearly continuous development without the need for frequent app resubmissions.
By default, it relies on string-based keys, lacks type safety, and may lead to silent failures, wasted budgets, or broken A/B tests.
Type safety, scalability, cleaner architecture, faster development cycles, and reduced risk of business failures.