Mobile onboarding evolution: Part 1. From app-embedded to backend-driven

Sasha Zinchuk
Flo Health UK
Published in
9 min readJun 17, 2024
By Sasha Zinchuk, Product Manager, and Aliaksei Talankou, Software Engineer.

Introduction

Before diving deep into details, we want to give you an overview of Flo’s domain, scale, and challenges. This information can help you understand why our onboarding is so complex and diverse.

Flo is a super app with various user segments and goals: cycle tracking, getting pregnant, pregnancy tracking, sex life, socializing in anonymous chats, etc. Moreover, we recently launched a Partner Mode feature with 1+ million MAU in 2024, meaning we are no longer only a female health app.

MAU: 60+ mln, 40% revenue from onboarding
The scale of onboardings at Flo

As you see, we have a huge organic traffic share, but because of privacy concerns, less than half of the audience allows their activity to be tracked on third-party services. As a result, we often lack information about new users installing the Flo app, such as their age, goal, health conditions, etc. Is it a new user or a user with previous experience of using the app? So many questions to ask, and all these user segments react and convert differently given the same series of onboarding screens.

User segmentation during onboarding

That’s why our onboarding has a lot of branches, depending on the main user goal and subgoals, with the total number of screens up to 400. Our product team is constantly experimenting (~5 hypotheses per 2-week sprint), which adds even more complexity to the onboarding content and logic.

Historically, Flo onboardings have different flows depending on the following user attributes:

  • Platform: Android, iOS, Web. The last one is server-driven by nature, so it’s not the focus of this article.
  • Locale: EN, top 5, other 15. Experiments are primarily conducted in English, then localized and tested for success in other regions.
  • Source: from app stores, from web to app, goal switching (e.g., when an existing user switches from tracking their cycle to tracking a pregnancy)
Flo onboarding experience

Initially, our application’s onboarding process was implemented using a series of hardcoded screens. The simplicity of this approach came at the cost of slow iteration and limited experimentation.

To enhance flexibility, we developed a domain-specific language for describing onboarding screens, which were integrated directly into the app. Though this embedded solution effectively addressed templated screen creation, it also introduced several issues we will explore in the following section.

Problems with app-embedded onboarding

Developers added new code and configurations to mobile repositories to deliver a new onboarding screen, experiment, or branch. Each sprint (two weeks), we released a new app version containing all these onboarding changes. Sounds normal, right? So, which problems pushed us toward server-driven onboarding?

Mobile app release cycle
  • Release-dependent experiments and fixes in onboarding.
    The mobile release cycle is too long, and product managers need to be able to react quickly (e.g., verify a fresh hypothesis, fix wording because of localization or legal issues, etc.).
  • Time to detect an error in experiment configuration > 1 day.
    Analytical systems are not real-time, new build reviews and rollouts can take days, and in cases of misconfiguration, you can only figure out that users are not attributed to all experiment groups the next day or even later.
  • iOS and Android engineers were bored by editing huge JSON configs.
    Mobile engineers wanted to solve interesting problems and build new experiences, not spend every sprint configuring flows with template steps.
  • Cognitive complexity — iOS onboarding had more than 350 steps and 500 transitions.
    Some developers used different ad hoc tools to visualize onboarding graphs on their local environments, but no generic solution existed. Product managers and quality assurance used a Miro board to keep track of all onboardings and avoid getting lost.
Just a fragment of the iOS onboarding

To solve these problems, we decided to try to move our onboardings to the server, especially given that we have a similar approach in other parts of our app, and to add a visual editor afterward.

Transition to backend-driven JSONs

As a foundation, we agreed on a common contract with a dictionary of steps and an array of transitions with conditions. This simple data model allows you to build a graph view using ReactFlow, mxGraph, D3.js, and other visualization libraries.

{
"steps": {
"initial": {"type": "initial"},
"step1": {"type": "feature_card", ...},
"step2": {"type": "question", ...},
...
},
"transitions": [
{"in": "initial", "out": "step1", "condition": []},
{"in": "step1", "out": "step2", "condition": ["tag1", "tag2"]},
...
]
}

The JSON files held information about different step types (such as questions, feature cards, authentication, age input, etc.), step layout, and transition logic. Android and iOS apps contained an engine that could read such files, validate them, and display a series of app screens depending on valid conditions.

Onboarding screens as a directed graph

After we implemented the contract, it was time to pick how the client would receive onboarding from the server.

Download modes

We discovered that onboarding configs were quite huge (up to 10,000 rows in JSON) and considered three options for downloading them:

  • Download the whole JSON at once. This was the simplest and cheapest option because there was only one request to the server per user. The size of the JSON file was the only concern, as it impacted download times.
  • Download in chunks, like in a chat. A more sophisticated solution, with higher infrastructure costs and complex logic.
  • Use a hybrid approach. Download the most important or popular flow upon app start-up and others on demand.
Hybrid downloading mode

We followed the Lean methodology and chose the first option: download the entire onboarding configuration at once and address any issues as they arise.

So, what happens on the first app start? We decided to:

  • Start downloading the onboarding during the initial animation
  • Stop waiting for a response when the user hits Next after accepting all policies
Downloading of onboarding (in debug build)

Therefore, our users have roughly eight seconds to download onboarding from the server, and if there is no response or internet connection, the embedded onboarding is applied as it used to be.

Survey Engine as onboarding backend

To manage onboarding configs on the backend, we bootstrapped a new service called Survey Engine. Its purpose was to hide all the complexity of onboardings from the client and serve them via REST API. We decided not to limit ourselves to the onboarding domain name, as we planned to broaden its scope of responsibility, incorporating in-app quizzes, polls, and more.

During the initial phase, we stored JSON files directly in the repository code, organized them in folders based on the app version, platform, and locale, and moved a call to the Experiment service from the app code to this new service.

Interaction with the backend

The overall workflow looked like this:

  1. The client calls the Survey Engine service.
  2. Survey Engine gets all valid experiments for this particular user.
  3. Survey Engine finds onboardings that match user attributes and experiments.
  4. Survey Engine sends a response with onboarding JSON and all experiment details to the app.

Server validation

To ensure that our users receive accurate data, we implemented a list of validations, such as:

  • Ensuring that steps and transitions are unique in a graph
  • Verifying that there are no cycles
  • Matching a list of steps with a list of transitions
  • Confirming that all steps in a graph are connected

Our backend service was designed to catch and highlight all potential errors in one pass, ensuring that any issues with the configuration are clear and easy for users to understand. Eventually, if there are any errors detected, we will not release the configuration to the users.

Informative error message with root causes

With automated validation of onboardings in place, our next step was to release them for users carefully.

Launch through an experiment

Like any other feature at Flo, we launched the server-driven onboarding under a feature flag, gradually rolling it out first on the Android English version: 5%, 10%, 50%, and 100%.

First rollout notification

We tracked all the tech metrics and the main onboarding business metrics: conversion to subscription, number of trials, progress on key funnel points, and number of registrations.

This experiment was different because success would mean that none of the metrics showed a significant statistical difference.

Example of a “successful” experiment — no statistically significant changes detected

After the rollout on Android was successful, we extended it to all other locales and repeated the same actions on iOS.

Client analytics: Local vs. remote onboardings

Since server onboardings contain the latest updates and A/B tests, we wanted most users to receive them. To measure our success rate, we added a new analytical event with the following parameters:

  • Onboarding type: remote or local
  • Return journey: false (first launch) or true (second+ launch)

Based on this event, we built a dashboard showing the total number of onboardings and the percentage of remote ones for the first launches and return journeys.

Onboarding analytics dashboard

Our findings so far:

  • On app start, 5% to 10% of users are offline or have a very slow internet connection. These numbers correlate with the measurements of other teams, for example, when existing users log symptoms.
  • Onboarding JSON size (gzipped) up to 300kb (10,000 lines) doesn’t influence the download success rate much.
  • Users with a short initial animation (2 seconds) have a similar percentage of downloaded remote onboardings as the other segments with a long animation (8 seconds).

In addition to these analytics, we wanted to ensure that our server-side onboarding was running smoothly and effectively, so we added real-time tech metrics.

Observability on server side

From the beginning, we leveraged Grafana to monitor what was happening with our onboardings. We tracked standard RED metrics (rate, errors, and duration) and set alerts for spikes:

  • Total number of requests
  • Number of successful 200 and failed 4xx and 5xx responses
  • Requests and responses by app version. It was very useful to understand how many old versions we need to support and how quickly onboarding users got the new version.
Requested onboardings traffic

The charts above show that iOS users started getting the new app version within hours. For Android, it was about a week due to a gradual rollout. On both platforms, it makes sense to support the two latest app versions. The share of users with older app versions is insignificant, and they can use the embedded onboarding instead.

Continuous onboarding development

We quickly realized a problem with frequent changes to JSON files in our mobile clients’ repositories. To prevent delays for the onboarding product teams during our migration, we decided to make the mobile repositories the single source of truth for configurations. Initially, we manually copied the updated JSONs to the backend repository, but the changes were happening so often that we couldn’t keep up. We created scripts to compare our server JSON files with those in the mobile repositories to solve this. If any differences were found, the scripts automatically generated a pull request.

Pull requests created by the script

As a result, we achieved a constant delivery of all onboarding updates to users without interruptions for the product team.

Results

Problems solved ✅

  • Dependency on store releases was gone: any changes to onboardings could be immediately delivered to the client.
  • We’ve proved the concept of server-driven onboardings and gained valuable experience and insights into metrics, client behavior, new version releases, and old version decommissioning.

Left to do 🚧

  • Visualize onboarding flows
  • Delegate onboardings and experiments setup from developers to business users
  • Add real-time experiment traffic monitoring

In the next series

We will uncover the migration of onboardings to Contentful, which made their creation smoother and less stressful. Stay tuned!

By Sasha Zinchuk, Product Manager, and Aliaksei Talankou, Software Engineer.

--

--