There’s a lot of complexity in the world. Mobile applications often simulate or enhance real world processes but they can make them unnecessarily more complex. Real life is already messy enough, so an app that makes something harder is likely to be deleted quickly. While many software developers revel in complexity, normal humans apparently don’t? Take math, for example. Some people evidently don’t enjoy relaxing in the tub with a good nonlinear partial differential equation. ¯\_(ツ)_/¯ But, fine, most users generally don’t like excessive complexity. Okay, users love simplicity!
So a mobile app is at its best when it insulates users from complexity, the tedious or difficult processes required for its function. Turns out one of the lesser known Laws of Thermodynamics is the Conservation of Complexity. If we want to make the user’s experience in an app simpler, we pay for that with increased complexity during design and development. When you truly care about the apps you make, it’s a small price to pay to make them the best they can be.
We recently shipped an app we truly care about that we developed with our partners at Planned Parenthood. Spot On is a beautiful app for tracking periods that looks simple and successfully conceals plenty of complexity. While recording and predicting details about menstrual cycles has some fun challenges, we were able to tackle some really exciting complexity by helping the user manage their birth control method.
The primary interface of Spot On allows users to tell the app details about their day, including whether they took their pill or changed their patch, what moods or symptoms they’re experiencing, and if they’re having their period. At the center of this view is a messaging area that is populated by a system we lovingly call AUNTi. Here the app has a bit of conversation with users by asking questions or sharing interesting bits of information. The key to AUNTi’s success is relevance and context. “Hey there, did you take your pill today?” is relevant only to pill users, not patch or IUD users, and only in the context that it’s today and the user hasn’t taken their pill yet.
Implementing this system led to comparisons with a beloved book series from decades past, the Choose Your Own Adventure books. Every few pages the reader is faced with a choice: to battle the giant lizard turn to page 96, to take an epic selfie with the giant lizard turn to page 98. The reader takes control and becomes part of the story. There are a limited number of choices, but the reader gets to choose.
To represent Spot On’s path of choices, we started with lots of flow charts representing the clinical considerations for each method (what if you missed yesterday’s pill? And the one from the day before?) which provided scripts for our adventurous users to follow. The problem is that scripts depict a linear, one-way flow of time. We wanted to allow the user to use the app however they liked, so if they don’t visit the app for a day or two and return to catch up, they can answer today’s question (Have you been keeping up with your pills?) or back up in time by a day or two and answer questions there first. When they return to the present, today’s question may have changed based on the earlier day’s answers. It’s like creating a Choose Your Own Adventure book that makes sense even when the reader randomly jumps around from page to page.
When Spot On launched, there were more than 150 options that could be displayed in the AUNTi area. The conditions for displaying each option varied in complexity but were all dependent in some way on the state of the app, like the user’s current birth control method, if the day in question is today or in the past or future, some nitty gritty details about answers to earlier questions, and the lunar phase (just kidding).
A flow chart lends itself to if/then/else conditionals and the quantity and extent of flow charts involved made it immediately obvious that the jungle of conditionals required by that approach would make even the most ardent complexophile break into a sweat. Instead, we developed a simplifying approach that blends the Boolean true and false values of various states of the app with a value representing the priority of each option. The Boolean states enforce the required conditions for each option while the priority values decouple the conditions of one option from all of the others. For example, the option “You didn’t log yesterday’s pill! If you missed it, be sure to take both yesterday and today’s pill,” has a priority described by the first equation below:
This priority calculation reads like a long sentence: The day is today and the user is on the pill method and they haven’t entered if they took their renewable (pill) yet, and so on. Each of these properties evaluates to a Float number. For example, dayIsToday is 1 if the selected day is today, or 0 otherwise. The condition properties are multiplied together with the maximum priority (92 in this case). If any condition is false, the resulting priority is 0. If all of the conditions are met, the priority is 92. The maximum priority values decouple the logic of options from other options, so the example above doesn’t need to take into account whether the pill type is progestin or not or if the user has an IUD instead. For example, we need to present a different option when the user is on a progestin-only pill. The priority for that is represented by the 2nd equation above.
Why Float properties representing Bool and not Int? Because Floats enjoy complexity more than Ints. Floats allow representing the grey areas between true and false. Some priorities can decay or grow over time. And it seems only natural to relate cycles with sine waves, right?
To determine the AUNTi option to display at any given moment, the priority of all of the possible options are computed, sorted, and the option with the highest priority wins. This started out as a simple Swift line like:
There’s a bit more complexity in the current version, as there usually is. For example, if multiple options share the same highest priority, the app picks a random option from those allowing us to vary the content presented to keep the app feeling fresh.
This decoupled conditional/priority system made the task of setting up all of the AUNTi options relatively simple. To understand why we see a specific option at any given point, we created debugging helpers that could be toggled on and off with breakpoints. When on, one breakpoint would print all of the options (identified by unique key strings) and their priority values, sorted by priority. The one at the top won the lottery. If a different option should have appeared instead, a glance at this logged output usually led us quickly to the issue. Should priorities be adjusted, or is the product zero? In the second case, we toggled on the other breakpoint to see the state of all of the AUNTi properties like dayIsToday. With this information, we quickly identified what wasn’t working by comparing each item in the priority conditionals computation with their current state. Because of these clearly logged insights into the inner workings of the app, non-developer team members who had the best understanding of the proper clinical flow through the app’s adventure could easily identify why the wrong options won the lottery and clearly convey the cause to the developer team.
So how do we manage complexity during mobile development?
1. Build a dream team
Reducing complexity takes a passionate dedication to creating amazing experiences by embracing the complexity, studying it, loving it, and ultimately destroying it. Surrounding yourself with pros who care as much as you do is the first step. We’re always looking for bright stars at Small Planet, btw.
2. Break it down
Many problems seem dauntingly complex as a whole, but finding ways to break them recursively into smaller chunks will ultimately lead to a set of accomplishable tasks. Sometimes the divisions are obvious, but seeing where best to make the breaks comes with experience.
3. Test a lot
When you get chummy with a complex system, it’s easy to forget what it’s like to think about that system for the first time. Testing an app with real users during development usually reveals some hard truths about your decisions and will make you rethink and recode and make a better app. It’s time consuming and expensive so we don’t always have the luxury of testing as much as we’d like, which makes it wonderful to work with partners like Planned Parenthood who value and prioritize testing.
4. Little things matter
Not every simplification has to be a big deal. We’re adding the ability to read HealthKit data for an upcoming update. A small simplification occurs when the user has entered period data in another app that syncs with the Health app. When that user first runs Spot On and gives read permission for HealthKit early during onboarding we’ll have their period data ready and waiting so that when they get to the question “When was the first day of your last period?” the date picker will be pre-populated with the value from HealthKit. It’s a little thing, but it makes the user’s experience simpler and the more often that happens, the happier they’ll be.
5. Find the balance
We live in a world with scopes and budgets and timelines, so we can rarely add all the features or crush all of the complexity we want. Lowering user-facing complexity takes time and will result in fewer shipped features. Having stakeholders understand this compromise and getting them on board with having fewer, better features is sometimes a tough sell, but when it happens the results can be amazing.
As a developer with programming experience spanning three decades using dozens of languages (some of which I’ve mercifully forgotten… goodbye FORTRAN 77 in particular), I’ve seen my share of complexity. Looking back over that span, Spot On may well hold the title as the most complex app I’ve ever worked on. It’s definitely the one I’m most proud of. The combination of a top notch team, relentless passion, and really good pizza can convert complexity into amazing apps. But, yeah, maybe simplicity is good after all.