There is always a tradeoff we need to resolve, between flexibility and architectural complexity. Flexibility enables agility. And agility means responding quickly to our users' needs, which is our ultimate goal while building tech products.
Today, I'll show you two of the many possible ways of providing flexibility to mobile apps, learning from our users in the process and responding to their behavior.
Before starting, it's important to note that, if our purpose is to learn from our users, and respond to their behavior, in order to improve their experiences, then a pre-requisite for doing that, is to have a way to learn from our users!
Analytics are the tool for doing so. I've used several services for mobile analytics, but here is a list of the things you could benefit the most from:
- Events: You can track how many users have tapped a button, or have completed a certain flow. All the analytics services out there support events and dashboards to see those events.
- User journeys: I love the ability to see the timeline of the events performed by a specific user. It lets you understand exactly how your users are using your app, in which part of the flow do they feel frustrated, etc. This is a great feature. Amplitude has good support for this.
- Crash logs: You need to understand how much and why your app is crashing. Crashlytics is the biggest exponent for this, and I highly recommend adding this to your workflow.
Now that we know how our users are interacting with our product, when do they feel frustrated, and why and when the app stops working and crashes, we can work on the product to provide our users solutions for those frustrations, or provide a better experience overall.
I define flexibility as the easiness for our product to change its behavior to adapt to our users needs.
Flexibility is important for every software product, but it's REALLY important for mobile apps, given the time you need to wait for the apps stores revisions before you can send a patch, and the time it takes for our users to update the app on their phones. Users patience is a very limited resource and we can't waste it.
There are many ways to make our product more flexible, but it's important to note that flexibility comes at a cost. We can't just make the app fully remote configurable. It's expensive. And the cost we have to pay for flexibility is architectural complexity. And architectural complexity can easily turn into technical debt. Beware of it.
A great way to fight against architectural complexity (and therefore, technical debt) is to only provide flexibility and remote configuration on the parts of the code you know it could be useful to tweak in the future. YAGNI.
Now, enough introduction. Let's jump into the actual techniques.
This tool has proven to be invaluable for us in the last couple of companies I've worked for, once the product is in the wild and we want to react to how our product is performing.
A feature flag is a value that can be remotely configured for the users of your app according to a certain criteria.
A feature flag is associated to a key, and have have several values for different types of users.
So, for instance, imagine we are developing a social network, and we decide it would be a great idea to add a flow to edit videos before uploading them. Now, that could be a great idea, but it's also risky. Things can go wrong, your app could crash if you have made a mistake while programming it, or maybe the flow is so complicated your users ends closing the app without uploading any video at all.
We know we can track how our users are using our app using analytics, but it's a great idea to also add a feature flag with an associated boolean value, so for 20% of our users, we "enable" the video editor, while the remaining 80% of our users continue viewing the old video uploading flow. If the 20% of our users have better analytics results than the remaining 80% (that means, more videos uploaded, or better reactions for those videos), then we can make the feature flags rate to 50%/50%, or even a 100%/0%.
Now, I'm oversimplifying. Feature flags are much more powerful than just that. Instead of sending an on/off boolean flag, we can associate a full json value for each users group, with more complex configurations. Or even having more than 2 users groups. We can have N users groups with N different json configurations, and then measure the analytics events for each of those groups.
Some notes for using feature flags:
- Release all user-facing features with a feature flag associated to it. You'll thank yourself having done this if something goes bad in production and you need to rollback or tweak something on the fly.
- Use analytics event dashboards to decide whether to advance feature adoption or to disable the feature if it didn't work as expected.
- Use crash reporting services to turn off the feature as soon as you note there is something crashing the app in users' devices.
I recommend Split for implementing feature flags, but you can use the service that best fits your needs.
Sometimes, you haven't added a feature flag for something and you note your app is crashing badly in our users' device. That's a nightmare and I don't wish this to anyone, but it's a good idea to be prepared for such a case.
As soon as you note this, work hard in order to deliver the simplest solution for that problem, and then upload a new version to the app stores. Keep in mind you will need to wait a couple of days for your patch to be approved by the stores reviewers.
This is a dramatic action, and I don't recommend doing this unless absolutely needed.
This is a hot topic in the mobile development landscape.
What is server driven UI? Well, you normally use a server to fetch for domain objects. You ask the server for the comments your users have done on a post, or the products they have added to their shopping cart, etc. This is a standard described in the REST guides, and works fine. However, it has a limitation. You can't define how those objects are going to be presented in the UI from the backend. If you work in a backend service, you just take care of sending the objects your client requested, and it's a client responsibility to correctly present those objects in the app UI.
Server-driven UI (or backend-driven UI, or SDUI for short) is a technique that does something different. If you're developing a newspaper app, for instance, instead of receiving a list of articles and defining how the user will see those articles, you will receive what components you should display in the UI and what texts, colors, and data you should display, even the navigation and other actions can be defined in a SDUI service!
SDUI is a great way for providing remote flexibility. Does certain component bothering your user, or is a component far below in the screen not being clicked by your user? No worries! Let's just tweak the server response so that component is going to appear nearer the top of the screen, and let's see if the analytics dashboards improve after that.
So far, so good, we've explained what flexibility is and why it is important. We've talked about analytics services and three techniques to fix things on the fly, limiting the influence the app stores review cycles in the our application.
Again, I need to warn you. Flexibility comes at the expense of architectural complexity, and this is a great example. Nothing is free in software. Becoming a better software engineer means becoming better at making those deals.
Let's finish linking one thing, the Spotify's engineers learnings on the server driven UI approach they have taken in the past, what worked, what didn't and the best practices they suggest for doing this: https://www.droidcon.com/media-detail?video=352688824
Experiment, learn and improve yourself. Thank you!