In the last few months, we have been thinking about implementing some solution that would allow us to set Feature Flags dynamically from a third party service to control what features are accessible in our application.
We have analyzed several solutions both self hosted and Software as as Service and eventually we decided to go with Firebase Remote Config. However, this implementation has proven to be more difficult that we initially imagined.
In this article, I would like to show you how we have implemented Firebase Remote Config for handling Feature Flags so that you can implement it more easily (and also understand all the caveats that this solution comes with).
What are Feature Flags?
As Brian mentions in his article for Launchdarkly: Feature flags are a software development concept that allow you to enable or disable a feature without modifying the source code or requiring a redeploy.
https://launchdarkly.com/blog/what-are-feature-flags/
This feature can be especially useful in several cases such as:
- Disabling a functionality that stopped working
- Release a new version with a functionality hidden as it is not ready yet but already developed
- Enable certain functionality for certain users only (A/B Testing)
But in simple words, Feature Flags are boolean values that decide in runtime whether certain thing should be visible/accessible or not.
There are few advantages of using Feature Flags including:
- Trunk based development – There is no longer a need for maintaining multiple long-lived feature branches.
- Manage the feature lifecycle – Controlling who has access to a feature enables teams to control the full lifecycle of that feature.
- Remove the stress and risk around releases – Feature flags reduce the stress around releases by allowing faster resolution of incidents
In our research, we have analysed several Feature Flags solutions (including Launchdarkly) but eventually we decided to go with Firebase Remote Config as our company relies on Google Cloud Platform so another solution from the same Cloud Provider was a good idea in the end.
What is Firebase & Remote Config
Firebase is a set of backend cloud computing services and application development platforms provided by Google. It hosts databases, services, authentication, and integration for a variety of applications, including Android, iOS, JavaScript, Node.js, Java, Unity, PHP, and C++
Firebase allows Frontend developers to easily interact with backend stuff such as a database, authentication, storage, etc.
Saving and reading records in the database is simple as:
var database = firebase.database();
// write
database.ref('users/' + userId).set(user);
// read / listen
database.child("users").on('value', function(snapshot) {
// ...
});
Firebase Remote Config is a cloud service that lets you change the behavior and appearance of your app without requiring users to download an app update. When using Remote Config, you create in-app default values that control the behavior and appearance of your app. Then, you can later use the Firebase console or the Remote Config backend APIs to override in-app default values for all app users or for segments of your user base. Your app controls when updates are applied, and it can frequently check for updates and apply them with a negligible impact on performance.
There is a great introduction video about using Firebase that you can check below:
How to use Firebase Remote Config for Feature Flags?
To be honest, it is a bit tricky :D
There are actually two ways of how you can use Firebase Remote Config to enable Feature Flags in your application -> In the Browser or on the Server.
The next sections of this article assume that you have already installed Firebase in your application. If you haven' t done so yet please refer to https://firebase.google.com/docs/web/setup#add-sdk-and-initialize.
Using Firebase Remote Config JS SDK (browser)
This is the easier approach that should be useful for smaller and less complex applications.
- Initialize the App
import { initializeApp } from "firebase/app";
import { getRemoteConfig } from "firebase/remote-config";
const firebaseConfig = { // config };
// Initialize Firebase
const app = initializeApp(firebaseConfig);
// Initialize Remote Config and get a reference to the service
const remoteConfig = getRemoteConfig(app);
- Set in app default values.
remoteConfig.defaultConfig = {
"new_feature": false
};
- Get value of flag
import { getValue } from "firebase/remote-config";
const val = getValue(remoteConfig, "new_feature");
- Fetch and activate
import { fetchAndActivate } from "firebase/remote-config";
fetchAndActivate(remoteConfig)
.then(() => {
// ...
})
.catch((err) => {
// ...
});
As I mentioned in the beginning, this solution should work well for a simple applications. Why? Because more complex apps can have more advanced needs like the ones mentioned below that this browser approach cannot solve:
- Firebase by default sets a throttling limit https://firebase.google.com/docs/remote-config/get-started?platform=web#throttling that disallows too many requests to fetch the data from Remote Config. This in general is a very good approach in web development to limit the amount of requests to not allow overloading of the service and making it unavailable. However, the problem here is that this remote config is supposed to be accessed many times (imagine that you have added a feature flag for a HeroBanner component that is on your homepage - every user accessing your website will be fetching Remote Config after each page reload).
- Firebase Remote Config JS SDK (browser version) caches the response received from Remote Config for each client in the browser. This means that each user accessing your website will have their own version of cached response from RC. You can try to disable this caching feature by setting:
remoteConfig.settings.minimumFetchIntervalMillis = 0;
but then still you will encounter the problem mentioned in the first point (throttling) that will even throw an error for rate limiting. And this is indeed a problem as with solution such as this one, you should be able to invalidate cache globally so that each user accessing your website will get the most up to date remote config (otherwise some users could still see the staled content while others would be able to access the new content that would be really bad for User Experience).
But don't worry, there is a way to solve this issue and it is described in the next section :)
Using Firebase Admin to access Remote Config on the Server
This approach is a bit more difficult to implement but it enables feature flags in your application and solves the issues mentioned above.
- Initialize the admin app with
service-account.json
import admin from 'firebase-admin';
import { resolve } from 'path';
const pathToServiceAccount = resolve(__dirname, 'service-account.json');
admin.initializeApp({
credential: admin.credential.cert(pathToServiceAccount),
});
this.remoteConfig = admin.remoteConfig();
- Fetch the flags from Firebase RC on initial load of the application
const template = await remoteConfig.getTemplate();
let flags;
Object.keys(template.parameters).forEach((key) => {
flags[key] = template.parameters[key].defaultValue.value;
});
- Create two endpoints
1. One for fetching the flags that are stored in memory (cached)
2. Second for fetching the actual flags from Firebase RC and storing them in memory (remember to secure this endpoint with some kind of secret/apiKey to disallow unwanted invalidation or rate limiting to Firebase)
This solution would allow you to use Firebase Remote Config for Feature Flags, avoid being rate limited, and have one global cache that can be invalidated for all users easily.
Summary
As you have seen above, there are two ways of using Firebase Remote Config for Feature Flags. Depending on the use case the first one might not be available but second one should work for all scenarios. If you are developing a fullstack web application, I would definitely recommend the second one.
And about Firebase Remote Config in general, I really enjoy working with this tool. For now, we are using it for storing boolean values only, however, Remote Config allows us to store even numbers and string. Imagine what you can achieve with it ;)
See you next time and take care!
Top comments (9)
@jacobandrewsky Nice write up! You seem to be the right person to get feedback from for a project I'm currently working on (tower.rocktlab.com) I basically want to offer a simplified but real-time remote configuration set of feature along side with some others that can improve the development process. Would you mind sharing your thoughts on this? I would love to read what you think about it
Hey Sebastian,
Sounds great, could you send me access to it so that I can take a look? :)
Hey @jacobandrewsky this is currently under development, but made a few updates to the landing so that it is easier to understand the main features! I will definitely let you know when the site is ready!
Awesome, keeping my fingers crossed for the success :)
How do you actually set the config for users though?
Enjoyed the reference to trunk based development. Nice work Jakub.
In our app, we have one set of users but if you want to use it for A/B testing for example, you could always add string values to remote config rather than booleans and consume theses string values in your frontend for example:
if user.type === remoteConfig.userType => show experimental section
If that is not what you were asking for, could you please clarify? I will try my best to answer :)
Thanks Jakub. I was asking more on the admin side. Feature Flags initially need to be created, right? they're not created by the browser/client but rather on a backend/backoffice to declare "FeatureA" so I was wondering where does that "admin" happen.
Yes, they need to be created in Firebase Panel and can be configured by using IAM firebase.google.com/docs/projects/...
Gotcha!