Slide 1

Slide 1 text

Bangkok From Feature Flags to Personalisation with Firebase Remote Config Somjintana Korbut (@naluinui) Firebase GDE

Slide 2

Slide 2 text

Release and Monitor Crashlytics Performance Monitoring Test Lab Build Auth Cloud Functions Cloud Firestore Cloud Storage Hosting Realtime Database Engage Predictions Cloud Messaging Remote Config A/B Testing Dynamic Links In-App Messaging Analytics

Slide 3

Slide 3 text

Dev 💻 Test ✅ ✅ ✅ Submit for review Launch user Mobile development process 🐞

Slide 4

Slide 4 text

Firebase Remote Config - Update app behavior remotely - Audience and custom targeting - Key:Value pairs in the Cloud - No cost! Yes, it’s free.

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Feature flags, A/B Tests, Personalisation Version 1.1.0 Add donate feature Use case 1: Put new feature behind flag Donate Every penny helps Donate Version 1.0.0 Helps reduce release risks - Test with small amount of user before rollout - Kill switch £ 5 £ 10 £ 15 £ 20

Slide 8

Slide 8 text

1. Create remote config key-value in Firebase Console 1 2

Slide 9

Slide 9 text

2. Set up Remote Config import FirebaseRemoteConfig // initialize remote config let config = RemoteConfig.remoteConfig() #if DEBUG let settings = RemoteConfigSettings() settings.minimumFetchInterval = 0 // usually 12 hour caching - adjust this if needed config.configSettings = settings #endif

Slide 10

Slide 10 text

3. Set up default in-app values // add default config config.setDefaults(fromPlist: "remote_config_defaults")

Slide 11

Slide 11 text

4. Fetch and apply a value import FirebaseRemoteConfig struct SplashView: View { var body: some View { ... .onAppear { let config = RemoteConfig.remoteConfig() config.fetchAndActivate { status, error in showMainView() // TODO: log error if needed } } } } Ref: 🔗 Firebase Remote Config loading strategies

Slide 12

Slide 12 text

5. Use value in the UI import FirebaseRemoteConfig import FirebaseRemoteConfigSwift struct ContentListView: View { @RemoteConfigProperty( key: "donate_feature_enabled", fallback: false ) var donateFeatureEnabled: Bool .. Button("Donate") { donateButtonPressed() } .hiddenConditionally(isHidden: !donateFeatureEnabled) }

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

Feature flags, A/B Tests, Personalisation Use case 2: Targeting conditions - Internal test - Percentage rollout Donate Dev team & Internal users Other users

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

Targeted configurations 📦 Ready-to-use conditions: - App, platform - App version, browser - Langage, country - Date time - Random percentiles ✨ Google Analytics: - User property, User audience

Slide 17

Slide 17 text

fitness_goal: "getStrong" music_genre: "classical" is_signed_in: "true" favorite_food: "ceviche" pronounces_gif: "hard_g" User Properties

Slide 18

Slide 18 text

Test your parameters within dev team - Get the installation id to test on your device import FirebaseInstallations Installations.installations().installationID { id, error in print("installation id: \(id)") }

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

Internal test import FirebaseAnalytics let isInternalUser = user.email.hasSuffix("firefeed.dev") FirebaseAnalytics.Analytics.setUserProperty( String(isInternalUser), forName: "is_internal_user" )

Slide 21

Slide 21 text

Percentage rollout

Slide 22

Slide 22 text

Other use case for targeted configurations Localization content

Slide 23

Slide 23 text

Other use case for targeted configurations Seasonal promotion campaign

Slide 24

Slide 24 text

Other use case for targeted configurations Adjust game complexity for different group of users

Slide 25

Slide 25 text

Feature flags, A/B Tests, Personalisation Use case 3: Experiment Donate VS I think our donate button is very hard to see – changing position of it might help? Boss: Version 1.1.2 Experiment position of donate button Donate

Slide 26

Slide 26 text

Firebase A/B Testing - Optimize app with multivariate experiments - Automatic winner determination - Integrated with Analytics, Remote Config and FCM

Slide 27

Slide 27 text

A/B Testing: What & Why - Make data-driven decisions - Shift from “I think” to “I know” - Understand users opinions - Every experiment tells you something

Slide 28

Slide 28 text

A/B Testing: What & Why - Make data-driven decisions - Shift from “I think” to “I know” - Understand users opinions - Every experiment tells you something

Slide 29

Slide 29 text

Design an experiment ● Goal ○ Which position of the donate button will get more engage? ● Variants ○ Top vs Bottom ● Targeting ○ 20% of iOS users Donate Donate VS

Slide 30

Slide 30 text

Log analytic events Button("Donate") { donateButtonPressed() } ... func donateButtonPressed() { showingDonateView.toggle() FirebaseAnalytics.Analytics.logEvent( "donate_btn_pressed", parameters: nil ) }

Slide 31

Slide 31 text

Add config for button position (Firebase console)

Slide 32

Slide 32 text

Create experiment draft

Slide 33

Slide 33 text

Set up default in-app value

Slide 34

Slide 34 text

Add config for button position ... @RemoteConfigProperty( key: "donate_button_position", fallback: "top" ) var donateButtonPosition: String // Button at the top Button("Donate") { donateButtonPressed() }.hiddenConditionally( isHidden: donateButtonPosition != "top" )

Slide 35

Slide 35 text

Add config for button position ... @RemoteConfigProperty( key: "donate_button_position", fallback: "top" ) var donateButtonPosition: String // Button at the top ... // Button at the bottom Button("Donate") { donateButtonPressed() }.hiddenConditionally( isHidden: donateButtonPosition != "bottom" )

Slide 36

Slide 36 text

1 2 Test your experiment

Slide 37

Slide 37 text

Test your experiment import FirebaseInstallations Installations.installations().authTokenForcingRefresh(true, completion: { (result, error) in guard let result = result else { return } print("Installation auth token: \(result.authToken)") })

Slide 38

Slide 38 text

Start your experiment

Slide 39

Slide 39 text

Monitor the result

Slide 40

Slide 40 text

Monitor the result

Slide 41

Slide 41 text

Roll out!

Slide 42

Slide 42 text

Roll out!

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

A/B Test Messaging

Slide 45

Slide 45 text

Drop off

Slide 46

Slide 46 text

Reduce it to be 3 pages by merging page 3 and 4 together

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

Feature flags, A/B Tests, Personalisation Use case 4: Personalization (Infinite A/B Testing) Mate, people are having different tastes – why stick with one option that best for all? Version 1.1.2 Personalize suggested amount Every penny helps £ 5 £ 10 £ 15 £ 20 Donate Every penny helps £2.99 £4.99 £9.99 £12.99 Donate or

Slide 49

Slide 49 text

Remote config Personalization Beta - Create several use experience alternatives - Machine learning to automatically provide best one to each user - ML model continuously adjusts

Slide 50

Slide 50 text

“รับขนมจีบ ซาลาเปา เพิ่มมั้ยคะ” กาแฟ นม ขนมปัง เพดดิกรี Options Objective / Goal Maximum revenue

Slide 51

Slide 51 text

“รับขนมจีบ ซาลาเปา เพิ่มมั้ยคะ” Options Objective / Goal Maximum revenue กาแฟ นม ขนมปัง เพดดิกรี

Slide 52

Slide 52 text

A/B Testing ● 1 best option for all ● Fixed time window ● Help on decision making ● Personalized for each users ● Continuous running ● Optimize conversion objective Personalization

Slide 53

Slide 53 text

Feature flags, A/B Tests, Personalisation Use case 4: Personalization (Infinite A/B Testing) Mate, people are having different tastes – why stick with one option that best for all? Version 1.1.2 Personalize suggested amount Every penny helps £ 5 £ 10 £ 15 £ 20 Donate Every penny helps £2.99 £4.99 £9.99 £12.99 Donate or

Slide 54

Slide 54 text

Add config for donate amount ... @RemoteConfigProperty( key: "donate_amount", fallback: "5,10,15,20" ) var donateAmountsString: String var amounts: [String] { donateAmountsString.components(separatedBy: ",") } Picker( "Donate amount", selection: $selectedDonateAmountIndex ) { ForEach(0..

Slide 55

Slide 55 text

Log event for goal measurement func donateButtonPressed() { FirebaseAnalytics.Analytics.logEvent( "donate", parameters: [AnalyticsParameterValue: selectedAmount] ) }

Slide 56

Slide 56 text

Create personalization

Slide 57

Slide 57 text

No content

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

No content

Slide 60

Slide 60 text

That’s a wrap! ● Feature flagging ● Targeting conditions ○ Internal testing ○ Percentage rollout ● A/B Testing ● Personalization @naluinui fb.com/FirebaseThailand fb.com/groups/FirebaseDevTH youtube.com/FirebaseThailand medium.com/FirebaseThailand Resources