Programming

Writing a future-proof app

I’m not the biggest fan of apps.  As I last posted, a reliance on apps (or even mobile web sites) to reach mobile users indicates that your core site is not robust enough to reach users in their desired medium.  No matter how great you market your app, your web site is infinitely more discoverable and will be your first entry point for most customers.  Remember, mobile users want to do things now more than they want to do things in an app, so much so that Facebook has more mobile web users than Android and iPhone app users combined.

What keeps an app from being future-proof?

While apps give you more control over the user experience and let you create richer user experiences, the web gives you more control over the content that users actually see.  If you roll out a new feature on your site and your app you’ll get 100% adoption on the site and trickling adoption on your app.  Maybe more of an exponential decay function but you will have plenty of users using the old version of your app again before they upgrade.  This is bad news if you’re trying out a new way to monetize your user base.

Another negative implication of this is seen when you’re releasing new versions of your app web services.  Unless you’re a large API provider like Facebook or Twitter, you’re probably dealing with one or two web service consumers, namely your apps.  And yet, because you have no control over when users update, you are forced to provide backwards compatibility with every service change.  You’re either releasing versioned API calls, versioned endpoints, or packaging old and new data together.  This introduces maintenance overhead.

A third case is for big companies with multiple departments producing their own APIs and web experiences.  Imagine a “reset password” flow.  You have zero ownership of the service but it is a critical feature of your app if you don’t want to drive users to the web.  Going native, though, has some critical risks.  That team could, for any number of reasons decommission their API or at least the API version you’re consuming.  Legal requirements could also change, forcing you to rush out a new app release to add a disclaimer to the screen.  As a result you make the experience a web view, but you can’t do this for every part of your app. Users hate that.

If you’re calling a large number of services, you could end up with scenarios where you’re releasing every week just to address these types of issues, and you still could run into issues with users not being able to perform their desired task without upgrading.  This may be a bit hyperbolic, but I am sure issues of these sorts are cropping up in your development flow, keeping you from focusing on what you should be: making kickass apps.

So how do we write a future-proof app?

To be clear, when I say “future-proof”, I am not meaning your app will last forever or that what I’m proposing won’t degrade the experience for affected users.  What I am offering is a proposal that will keep your site working through the issues mentioned above without requiring you to sacrifice user experience or spend your whole life putting out fires.

Removing Features

First, let’s deal with the scenario where you want to add or remove features from your app on a whim.  Let’s first focus on removal, since it is easy and common-sense.  Removing a feature is simply a matter of your app asking the server what features it can use.  Imagine a service “menu.json”:

menu.json?lang=en_US:
{
    "main-menu": [
        {
            "name": "Activity Feed",
            "controller": "ActivityFeedController"
        }
        {
            "name": "Friend Map",
            "controller": "FriendMapController"
        }
    ]
}

The “Friend Map” team decides to decommission it.  Rather than pushing a new release, we simply go to a friendly internal UI and uncheck “Friend Map”.  The change immediately goes into effect across your portfolio: desktop web, mobile web, apps.

Adding Features

Adding a feature could be the opposite, imagine we removed “Friend Map” and added “Top Posts”. Our menu.json looks like this:

menu.json?lang=en_US:
{
    "main-menu": [
        {
            "name": "Activity Feed",
            "controller": "ActivityFeedController"
        }
        {
            "name": "Top Posts",
            "controller": "TopPostsController"
        }
    ]
}

The problem with this, of course, is that not all apps support “Top Posts”.  The most basic case would be to ignore that menu items if you don’t have the controller but we would much rather let everyone access that feature right away.

You fill in the gap by sending the user to a webview of the page instead.  Users who have not upgraded get the mobile web experience, which may not be as great but is easily better than nothing.  You could tack in an upgrade link to give users the option of opening the app store right away:

menu.json?lang=en_US:
{
    "main-menu": [
        {
            "name": "Activity Feed",
            "controller": "ActivityFeedController",
            "fallbackUrl": "http://mycompany.domain/mobile/activity-feed"
        }
        {
            "name": "Top Posts",
            "controller": "TopPostsController",
            "fallbackUrl": "http://mycompany.domain/mobile/top-posts"
        }
    ]
}

This actually puts you in a really good position, because, depending on business demands, you can deploy an experimental new app feature cross platform without having ever written a line of native code.  The menu.json model can also be integrated with logic to, for example, only display an item to a certain set of users or device types during a staged roll out.  Once we know the feature is here to stay, we can then add a native experience.

Protecting against web service hell

Web service hell is a catchall for what I mentioned before, scenarios where either a service you consume goes offline or you want to make breaking changes to your web service API without maintaining the old version for outdated apps.  There are actually two mechanisms I would recommend using.

The first provides the best user experience by extending “menu.json” even further.  We can feed the current web service API versions into the JSON.  When the app encounters a menu item, it will first check if the controller is available.  It will then ask the controller which API version it supports.  If it does not match the version from the JSON, it will skip the native component and render the webview instead.  To do this, we would need our JSON to look something like the below:

menu.json?lang=en_US:
{
    "main-menu": [
        {
            "name": "Activity Feed",
            "controller": "ActivityFeedController",
            "apiVersionRequired": "4",
            "fallbackUrl": "http://mycompany.domain/mobile/activity-feed"
        }
        {
            "name": "Top Posts",
            "controller": "TopPostsController",
            "apiVersionRequired", "2.3.7",
            "fallbackUrl": "http://mycompany.domain/mobile/top-posts"
        }
    ]
}

A weakness of this approach is that you may not always know that a service is changing.  In this case, your service should be able to tell the app that the request is no good and your app should be able to take this hint.  If your service is down, the service should return 502,  or at least 500 or 404.  In this case, your app would fall back to the fallbackUrl.  If the service is up, the app should include the protocol version in the request and the server should respond with something like a 400 response.  In the headers, it could include X-SERVICE-VERSION-REQUIRED and possibly X-FALLBACK-URL.  The fallback URL could provide more-specific data for your logs, since in reality you should have avoided this request.

HTTP/1.1 400 Bad Request
X-SERVICE-VERSION-REQUIRED: 2.3.7
X-FALLBACK-URL: http://mycompany.domain/mobile/top-posts?error=bad-version

In both cases, if you’ve already rendered an unpopulated view, it is best to give a user a message letting them know why you’re leaving it in order to avoid confusion.

Conclusion

While apps may not inherently provide the same level of control over user experience that you find in web, a blending of experiences (being readily able to switch between native and hybrid for any given flow) will allow you to provide a more rapid delivery of features.  This flexibility will allow you to prioritize native app work effort, expand the areas you feel comfortable creating native views, and weather the tide of hundreds of projects you may have zero control over or knowledge of.  Bottom line: a more stable resilient app and more time to focus on creating cool features.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s