Best practices for Remote Config fetch efficiency

Firebase Remote Config gives you flexible control over your app's behavior and appearance. This enables capabilities like feature rollouts and cross-platform A/B testing for your apps, all without deploying new versions or navigating multiple app store updates.

Whether you're prototyping, running a growing startup, or managing an enterprise application at scale, managing your network fetch volume is key to delivering a fast, responsive user experience. Efficient configuration management reduces cold-start latency, saves client data and battery usage, and prevents unnecessary network overhead. In addition, if your user base expands rapidly and drives up usage in the future, streamlining your Remote Config integration helps ensure that your usage remains efficient.

You can reduce client-side network request traffic significantly by refining how and when apps fetch parameters and when they activate them.

Shift to the "fetch for next session" pattern

A common pattern to avoid is using the fetchAndActivate call—which both fetches new values over the network and activates them—on every app launch combined with a short cache expiration period for previously fetched values (for example, 15 minutes to an hour). The mental model behind this approach is that every time a user opens the app, they always have the latest values fetched and applied. While there are times when an immediate update is necessary (for example, running a daily sales campaign or game promotion), it's important to balance that goal with the impact on your app's performance and fetch usage.

This approach forces fresh network calls every time the cache expires, generating high fetch volume for users who open the app multiple times a day.

Instead, consider using fetch and activate calls separately with a higher minimum fetch interval to adopt a "fetch for next session" model. You can still use fetchAndActivate with a higher minimum fetch interval since fetch only executes a network request if the cache is invalidated, but using the two calls separately helps solidify the pattern and establish it as a standard practice in your application development process. Additionally, by activating separately, you don't run the risk of applying configuration values mid-session and disrupting the user experience.

How this approach works

  • Activate immediately on launch: Apply configurations cached from the previous session instantly (0 ms network delay).
  • Fetch in the background with a longer cache (for example, more than 12 or 24 hours): Request updated configurations asynchronously to refresh the local cache for the next session.

How this optimizes fetch volume

A longer minimum fetch interval results in fewer fetch requests. Fetching new values for the next session and activating cached values for the current session means that your app loads instantly from the local cache over a longer validation period, leading to better user experiences.

For example, if you set minimumFetchInterval to 24 hours and a user opens your app 5 or 10 times in a single day, the SDK automatically satisfies launches 2 through 10 directly from the local cache—reducing that user's daily network request count from 10 or more fetches down to 1.

The following examples show what this implementation looks like for Android, Apple platforms, and web apps:

Android

val remoteConfig = Firebase.remoteConfig

// Set a 24-hour minimum fetch interval (86,400 seconds)
val configSettings = remoteConfigSettings {
    minimumFetchIntervalInSeconds = 86400
}
remoteConfig.setConfigSettingsAsync(configSettings)

// 1. Instantly activate values cached from the LAST session
remoteConfig.activate().addOnCompleteListener {
    applyAppConfigurations()
}

// 2. Fetch new values in the background for the NEXT session
remoteConfig.fetch().addOnCompleteListener { task ->
    if (task.isSuccessful) {
        // Optional: Activate values if needed
    }
}

iOS+

let remoteConfig = RemoteConfig.remoteConfig()

// Set a 24-hour minimum fetch interval (86,400 seconds)
let settings = RemoteConfigSettings()
settings.minimumFetchInterval = 86400
remoteConfig.configSettings = settings

// 1. Instantly activate values cached from the LAST session
remoteConfig.activate { changed, error in
    guard error == nil else { return }
    DispatchQueue.main.async {
        self.applyAppConfigurations()
    }
}

// 2. Fetch new values in the background for the NEXT session
remoteConfig.fetch { status, error in
    if status == .success {
        // Optional: Activate values if needed
    }
}

Web

import { getRemoteConfig, fetchConfig, activate } from "firebase/remote-config";

const remoteConfig = getRemoteConfig(app);

// Set a 24-hour minimum fetch interval (86,400,000 ms)
remoteConfig.settings.minimumFetchIntervalMillis = 86400000;

// 1. Instantly activate values cached from the LAST session
activate(remoteConfig).then(() => {
    applyAppConfigurations();
});

// 2. Fetch new values in the background for the NEXT session
fetchConfig(remoteConfig).then(() => {
    // Optional: Activate values if needed
});

Implement conditional "smart" fetching

If the "fetch for next session" pattern introduces too much latency between when you need to update Remote Config values and when they become available in your client apps, consider adopting conditional "smart" fetching.

To implement this effectively, avoid attaching fetch triggers to broad UI lifecycle hooks, such as every time a screen loads, a tab switches, or a view gains focus.

Instead, trigger fetch requests selectively based on explicit app actions or states, such as:

  • User sign-in events
  • Transitioning into specific user flows where your parameters are used (for example, entering a checkout funnel or leveling up in a game)

Conversely, avoid triggering fetch requests for routine actions like:

  • When a user opens the app or starts a new session
  • When the app transitions between background and foreground states

Next steps