Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Shipping Apps Confidently with Firebase

Shipping Apps Confidently with Firebase

53b72671be580e70c9795c7eaf35ac12?s=128

Subhrajyoti Sen

November 06, 2021
Tweet

Transcript

  1. Shipping Apps Con dently with Firebase KeepTruckin Subhrajyoti Sen DevFest

    Greece & Cyprus 2021 November 2021
  2. Crashes

  3. Crashlytics

  4. Crashlytics • Automatic crash reporting

  5. Crashlytics • • Automatic crash reporting But no limited to

    crash reporting
  6. Recording Non-fatal exceptions

  7. Recording Non-fatal exceptions try { // some code can throw

    an exception } catch (e: Exception) { Log.d(TAG, e.localizedMessage) }
  8. Recording Non-fatal exceptions try { // some code can throw

    an exception } catch (e: Exception) { Log.d(TAG, e.localizedMessage) }
  9. Recording Non-fatal exceptions try { // some code can throw

    an exception } catch (e: Exception) { FirebaseCrashlytics.getInstance().recordException(e) }
  10. Recording Non-fatal exceptions private class CrashReportingTree : Timber.Tree() { }

  11. Recording Non-fatal exceptions private class CrashReportingTree : Timber.Tree() { override

    fun log(priority: Int, tag: String?, message: String, t: Throwable?) { } }
  12. Recording Non-fatal exceptions private class CrashReportingTree : Timber.Tree() { override

    fun log(priority: Int, tag: String?, message: String, t: Throwable?) { if (priority == Log.ERROR && t != null) { FirebaseCrashlytics.getInstance().recordException(t) } } }
  13. Recording Non-fatal exceptions class MainApplication : Application() { override fun

    onCreate() { super.onCreate() Timber.plant(CrashReportingTree()) } }
  14. Recording Non-fatal exceptions try { // some code can throw

    an exception } catch (e: Exception) { FirebaseCrashlytics.getInstance().recordException(e) }
  15. Recording Non-fatal exceptions try { // some code can throw

    an exception } catch (e: Exception) { Timber.e(e) }
  16. Understanding Crashes Better

  17. Analytics

  18. Analytics • We normally use analytics in isolation from crash

    reporting
  19. Analytics • • We normally use analytics in isolation from

    crash reporting Usually PMs check the analytics and Devs check the crashes
  20. Analytics • • • We normally use analytics in isolation

    from crash reporting Usually PMs check the analytics and Devs check the crashes What if you can combine them to get a full view?
  21. Analytics

  22. Analytics

  23. Analytics binding.zoomImage.setOnClickListener { MixpanelAPI.track("Zoom button clicked") }

  24. Analytics binding.zoomImage.setOnClickListener { MixpanelAPI.track("Zoom button clicked") FirebaseAnalytics.getInstance(context) .logEvent("Zoom button clicked",

    mapOf("page", "map")) }
  25. Analytics interface AnalyticsProvider { fun track( analyticEvent: String, properties: Map<String,

    Any?>? = null ) }
  26. Analytics class FirebaseAnalyticsProvider( private val rebaseAnalytics: FirebaseAnalytics ): AnalyticsProvider {

    override fun track(analyticEvent: String, properties: Map<String, Any?>?) { rebaseAnalytics.logEvent(analyticEvent, properties) } }
  27. Analytics class FirebaseAnalyticsProvider( private val rebaseAnalytics: FirebaseAnalytics ): AnalyticsProvider {

    override fun track(analyticEvent: String, properties: Map<String, Any?>?) { rebaseAnalytics.logEvent(analyticEvent, properties) } }
  28. Analytics class AnalyticsManager { private val analyticsProviders = mutableListOf<AnalyticsProvider>() fun

    addProvider(provider: AnalyticsProvider) { analyticsProviders.add(provider) } }
  29. Analytics class AnalyticsManager { //... fun track(analyticEvent: String, properties: Map<String,

    Any?>?) { analyticsProviders.forEach { provider -> provider.track(analyticEvent, properties) } } }
  30. Analytics binding.zoomImage.setOnClickListener { analyticsManager.logEvent( "Zoom button clicked", mapOf("page", "map") )

    }
  31. Feature Flags

  32. What's a feature ag?

  33. What's a feature ag? if (isNewFeatureEnabled) { // allow access

    to shiny new feature } else { // prevent access to shiny new feature }
  34. Use cases

  35. Use cases • A/B Testing

  36. Use cases • • A/B Testing Rolling out new features

  37. Use cases • • • A/B Testing Rolling out new

    features Rolling out rewrite of existing features
  38. Use cases • • • • A/B Testing Rolling out

    new features Rolling out rewrite of existing features Merge Work-in-progress features
  39. Types of Feature Flags?

  40. Types of Feature Flags? • Static

  41. Types of Feature Flags? • • Static Decided at build

    time
  42. Types of Feature Flags? • • • Static Decided at

    build time Based on things like versionCode, buildVariant, etc
  43. Types of Feature Flags? • • • • Static Decided

    at build time Based on things like versionCode, buildVariant, etc Dynamic
  44. Types of Feature Flags? • • • • • Static

    Decided at build time Based on things like versionCode, buildVariant, etc Dynamic Can be controlled at runtime either locally using dev settings
  45. Types of Feature Flags? • • • • • •

    Static Decided at build time Based on things like versionCode, buildVariant, etc Dynamic Can be controlled at runtime either locally using dev settings Or remotely via services like Firebase Remote Con g
  46. None
  47. Show me code!!

  48. interface Con g { val key: String val default: Boolean

    val description: String }
  49. enum class FeatureFlags( override val key: String, override val default:

    Boolean, override val description: String ): Con g
  50. enum class FeatureFlags( override val key: String, override val default:

    Boolean, override val description: String ): Con g { NEW_CHECKOUT_FLOW( "checkout_ ow_v2", true, "Enable checkout ow V2 for trending items" ) }
  51. interface FeatureFlagProvider { fun getValue(featureFlag: FeatureFlag): Boolean }

  52. class FirebaseFeatureFlagProvider: FeatureFlagProvider { private val remoteCon g = FirebaseRemoteCon

    g.getInstance() override fun getValue(featureFlag: FeatureFlag): Boolean { return remoteCon g.getBoolean(featureFlag.key) } }
  53. class RemoteCon gManager( private val featureFlagProvider: FeatureFlagProvider ) { fun

    isFeatureEnabled(featureFlag: FeatureFlag) = featureFlagProvider.getValue(featureFlag) }
  54. if (remoteCon gManager.isFeatureEnabled(NEW_CHECKOUT_FLOW)) { // allow access to shiny new

    feature } else { // prevent access to shiny new feature }
  55. Using Feature Flags effectively

  56. Using Feature Flags effectively • De ne success metrics

  57. Using Feature Flags effectively • • De ne success metrics

    Less Crashes?
  58. Using Feature Flags effectively • • • De ne success

    metrics Less Crashes? Smoother experience?
  59. Using Feature Flags effectively • • • • De ne

    success metrics Less Crashes? Smoother experience? Implement using your Analytics library (like Mixpanel)
  60. Using Feature Flags effectively • • • • • De

    ne success metrics Less Crashes? Smoother experience? Implement using your Analytics library (like Mixpanel) Create dashboards to compare
  61. @iamsubhrajyoti https://calendly.com/subhrajyotisen

  62. Credits: UC Davis