Skip to content
/ firely Public

Firely is an A/B Testing overlay based on Firebase Remote Config

License

Notifications You must be signed in to change notification settings

busbud/firely

Repository files navigation

firely

Firely is an A/B Testing overlay based on Firebase Remote Config. It's a work in progress to simplify the integration and make the management of A/B testing XPs safer.

How does it work

This library, integrated in your gradle project, only requires:

  • A firely-config.json file that will contains the type of items, the keys, and the default value
  • A call to Firely.setup(Context context) from the Application.onCreate() method
  • One proguard rule
  • Set apply plugin: 'com.google.gms.google-services' in your build.gradle file
  • Make sure you have a google-services.json provided by Firebase in your project

firely-config.json file is organized in 3 main sections (for us, but it can have the "names" you want):

  • Feature Flags
  • Config
  • Experiments (or A/B Tests)

Here is an example of firely-config.json:

{
  "config": [
    {
      "key": "android_version_code_min",
      "default": 0
    }
  ],
  "feature_flag": [
    {
      "key": "refer_a_friend",
      "default": true
    },
    {
      "key": "promotion_url",
      "default": ""
    }
  ],
  "experiment": [
    {
      "key": "xp_button_pay",
      "default": "control"
    }
  ]
}

Firely is an Android library that come with a gradle plugin, firely-plugin. It will generate a FirelyConfig.kt file based on the firely-config.json, like the R.java android creates. The FirelyConfig.kt will contain Enums that match the configuration. You can then use these enums on Firely to get LiveVariable, CodeBlock, OrderedArrayBlock.

LiveVariable

Let's imagine I am using Remote Config to restrict my user to a minimum Android version on which they can run (otherwise they have to update the app). With Firely, I can instantiate a LiveVariable that will use this setting:

val minAndroidRemoteVersion = Firely.integerVariable(FirelyConfig.Config.ANDROID_VERSION_CODE_MIN)

FirelyConfig.Config.ANDROID_VERSION_CODE_MIN is generated by the plugin and the default value is

Now, anytime I need to get the last version that has been fetched, I just call:

val lastVersion = minAndroidRemoteVersion.get()

Here is another example with a feature flag:

if (Firely.booleanVariable(FirelyConfig.FeatureFlag.REFER_A_FRIEND).get()) {
    // Add the view
}

CodeBlock

Now I need to build out an XP that will change the text of a button.

Firely.codeBlock(Remote.Experiment.XP_BUTTON)
    .withVariant("billed_currency", "no_price")
    .execute(
        { advance.setText(getString(R.string.bb_payment_cta)) },// control
        { advance.setText(getString(R.string.bb_payment_cta_2)) },// billed_currency
        { advance.setText(getString(R.string.bb_payment_cta_3)) })  // no_price

NOTE: we are always using "control" as the default value and as the control group for A/B Tests.

OrderedArrayBlock

In the Busbud Android App, we use a lot of blocks and lists. Let's imagine you have N blocks of data in a page. You want to A/B test which one should go first and the order for all the others. A basic approach could be to have * N!* variants.

If we have three items: 1-2-3, 2-1-3, 2-3-1, 1-3-2, 3-2-1, 3-1-2

And while using CodeBlocks:

Firely.codeBlock(Remote.Experiment.XP_BUTTON)
    .withVariant("2-1-3", "2-3-1", "1-3-2", "3-2-1", "3-1-2")
    .execute(
        {
            addOne()
            addTwo()
            addThree()
        }, // control  
        {
            addTwo()
            addOne()
            addThree()
        },
        ... etc

Really inefficient.

Another approach is to use OrderedArrayBlock. You will use one Firebase entry:

{
  ...
  "experiment": [
    {
      "key": "xp_mypage_order",
      "default": "one,two,three"
    }
  ]
}
val mCheckoutXp =
    Firely.orderedArrayBlock(FirelyConfig.Experiment.XP_CHECKOUT_ORDER)
        .addStep("one") { addOne() }
        .addStep("two") { addTwo() }
        .addStep("three") { addThree() }

And you can control your A/B Tests from the Firebase Remote Config dashboard by changing the xp_mypage_order key.

three,one,two will then call addThree(), addOne(), addTwo(). You can use this to remotely control the order of lists.

Analytics

One of the highlights of Firebase is that everything is working together. In the documentation, Firebase proposes putting the values, manually, as a User Property:

(TODO update to match the proper usage of the latest version firebase remoteconfig )

val experiment1_variant = FirebaseRemoteConfig.getInstance().getString("experiment1")
AppMeasurement.getInstance(context).setUserProperty("MyExperiment", experiment1_variant)

That's nice, but it does not fit our needs. Putting the property at the user level means it will be erased over time and we will lose the information. Instead, we prefer to tag all the events with all the experiments that have been applied at the time the event is triggered.

We added a property on Firely to help with this:

InternalFirely.allPropsWithCurrentValue

And this property is updated each time we send an event and merged into the property list. Therefore we can track the configuration changes over time.

Use in the project

Load the correct version of the plugin on your top-level build.gradle file:

plugins {
    id 'com.busbud.android.firely' version '0.2.1' apply false
}

Then apply the plugin without mentioning the version on the modules you need:

plugins {
    // other plugins here...
    id 'com.busbud.android.firely'
}

You also need to import the library in your project. For that you need to add jitpack.io to your top-level build.gradle file

allprojects {
    repositories {
        // other repositories here ...
        maven { url 'https://jitpack.io' }
    }

And in the project you want to apply you should add:

dependencies {
    // other dependencies here ...
    implementation 'com.github.busbud:firely:0.2.1'
}

If you need to update the plugin on the Gradle Plugin Portal (ADMINS ONLY)

Currently this process is manual and it is not being done via CI. In order to do this you need to have access to this account.

  • Bump the version of library
  • Run the following command (keys are available on the account):
./gradlew publishPlugins -Pgradle.publish.key=<key> -Pgradle.publish.secret=<secret>

If you need to update the firely lib on Jitpack.io (ADMINS ONLY)

  • Bump the version of the library
  • Create a tag with the new version
  • Create a release on Github based on the newly created tag
  • Wait a few seconds and you should see your update here

About

Firely is an A/B Testing overlay based on Firebase Remote Config

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages