Monday, July 30, 2012

Making Apps Indistinguishable from Magic

Imagine yourself from 1990 transported to the present, and being handed a smartphone

Had you handed 12 year-old me a Galaxy Nexus and a Nexus 7, and I'd have assumed they were props from ST:TNG. Show Asphalt 6: Adrenaline to a kid who's been obsessively playing Test Drive II on his 33Mhz 386, and watch him shit his pants.

When I was 12 years-old, it was 1990. I was running a BBS out of my parents living room and the World Wide Web didn't exist, but I downloaded shareware from local BBSs and browsed USENet with my brand-new 14.4kbit/s modem.

I'd seen a mobile phone, but the first SMS message wouldn't be sent for another 2 years.

Today we all have handheld devices that operate by voice and touch, that let you take pictures, record video, watch movies, and play songs. If that would have been vaguely comprehensible to a 12 year old in 1990, throw in a quick demo of Shazam or an international video Hangout.

You're not building apps for a portable handheld computer with a cell radio-based Internet connection. You're developing apps for a magic box

When you watch a good movie you don't think about how it was made. It's only when something breaks the illusion: an actor glancing at the camera, a piece of the set falling over, or a boom mic dropping into frame, that you're reminded that you're not looking through a magic window, but at actors on a set.

As developers, our goal should be to provide an app experience so immersive, that 12 year-old me never loses the feeling of wonder while playing with the magic box from the future.

Hide the connection

Make it seem as though all the information your app provides is somehow magically stored within the device itself.

You don't stop to think how you're online until you need to wait for a download to complete or when a connection fails. Aggressively prefetch and queue-and-send messages when you're next connected.

It's your responsibility to ensure that network speeds and intermittent connectivity don't leak in to the user experience.

Hide the Internet

The Internet Protocol provides best effort delivery over a service characterized as unreliable. That means downloads will fail, connections will be dropped, and errors will be received.

Your users don't need to know or understand what this means or why it happens; implement silent retries with exponential back-offs to handle data transfer problems without interrupting your users, and display error messages only when they're actionable.

Hide your user interface

We learn through positive and negative reinforcement, so when you guess wrong Venkman gives you a shock, and you're that much more hesitant to guess the next time.

The best interface is the one you don't notice, where the very first thing you try does exactly what you want it to do, and any accidental actions that prove destructive are easily reversed.

Help your users establish and build trust with your app.

Hide the battery

Magic devices run forever without being plugged in. You can't stop the battery from draining all by yourself, but you can do your part to ensure it lasts as long as possible.

Hide the device

A good app includes a combination of dynamic layouts flexible enough to support any screen size. A great app considers how its core functionality is best served given the screen and hardware on which it's running.

That probably includes a series of different layouts, but might also require an entirely different user experience when the underlying hardware is radically different from a standard smartphone. A magic app seems as though it's designed specifically for whatever platform you're running it on.

Hide your app

A magic box provides a series of useful actions and functions: sending a message, looking at a map, or recognizing music.

To keep the magic alive your app should should be designed to serve a particular function, one that's obvious as soon as the app loads. Similarly, it should be available whenever you need it (even if you didn't realize you needed it), and should be accesible by clicking an intuitive icon, a widget, or a notification.

An alternative title

Sometimes you don't know what point you're trying to make until long after you've finished making it. Such was the case for me this year upon reflecting on my Google I/O 20212 session, "Making Good Apps Great", which - as it turns out - I should have titled: "Making Apps Work Like Magic."

13 comments:

Unfortunately you are talking about Android OS ... the magic is just surviving Google's hostile bots, scripts, non-existent customer service and getting your application running on a massively fragmented collection of devices, screens and hardware.

My Android apps run on more than 1500 devices without problems... and I'm doing it on my time for fun.If you follow the Android Design guidelines, your app should run on all Android devices without problems.If you're trying to develop your Android app with the same methodology as your iOS app, you'll fail.

To be fair: he never said magic was easy. All of the things he's mentioned take a lot of careful thought and hard work.

Now, I'm not saying that Android shouldn't be changed to make things better, just that it's current incarnation requires you to have some good coding and design skills to be successful.

As for your other points: yes Google definitely needs better customer support, but this personal blog is probably not the right place to discuss it. Send emails, call up their support lines, and then complain- loudly and often. If their customer support managers are even halfway decent they'll bring everyone's complaints to their managers, who will hopefully work on making things better.

As far as the fragmentation goes: I don't like it either, but that's what happens when you're allowed to choose from a large number of Android compatible devices. The alternative is to restrict things to the point where personal choice is lost and everything looks the same. In other words: everyone becomes boring.

My apps also support 1500 devices without application issues ... and I am doing it for fun, just like you. The root problem is with Google and Android developer advocates/support. I agree - send emails (to unmonitored email) fill out ignored support requests, etc. Do what you can to be heard as a owner, developer, user and fan. I like this blog and that is why I posted my comment -- because it welcomes them.

I find that its not the different screen sizes that are the problem, but the API versions, having such nice things as the Download service only available on API 10 and above, and various video codec or html5 support in different versions and trying to support devices all the way back to Android 1.5 is tough.

Apple seem to have struck gold with their developers whereby they can upgrade 80%+ of their devices to iOS5 or iOS6 it gives you a very easy platform to target.

Andrew, you can pretty much forget about Android versions below 2.2 at this point, and within a year you should be able to forget about below 2.3, which will be a relief for those of us developing with the NDK.

On the topic, for some types of apps to feel like magic, you really need instant touch response, both visually through animation and when applicable through audio. Only now with Jelly Bean are the latencies finally low enough that apps that generate audio responding to touch events can feel anywhere close to being magic, and it still could be better.

Absolutely. That's why Project Butter was such a big priority for Jelly Bean. Smoother UX, more dejanking, and better audio latency are going to continue to be priorities for the framework team in future releases.

Who is to say that Google will continue to certify devices w/ 2.3 on them? My guess is they will stop (have stopped?) certifying them.. That means if they want to release w/ 2.3 they don't get Google Play, etc.

I have to agree with the basic sentiment that "apps should be magic". But I have to disagree with the network example. The HTTP abstractions are what Joel calls "leaky abstractions". That is, it is SUPPOSED to be perfectly layered and abstracted so that TCP and HTTP really do provide reliable service, but we all know they do not. Meier does account for this. It is the way he accounts for it that raises my eyebrows. The user might know better than the programmer: he might know that the local WiFi is down, and the phone carrier is only offering 2G or GPRS at the moment, so he does not WANT to connect to the network. Meier's proposed solution of exponential backoff etc. does not accommodate this. The user needs to have a smooth interface even when saying to the app, "don't bother with the net, do what you can with local/cached data and don't bother me with net failures, I KNOW it is down".

For that matter, it is a lot easier for app writers to write 'magic' when the Google APIs work reliably. But on my G1 phone, for years I had the problem of its inability to keep up a network connection with my Belkin54G WiFi router. When I looked up the bug on the public database, I discovered others had reported the problem and the bug had not even been updated, far less fixed, in over two years.

Finally, in a similar vein, if Google is going to ask so much out of developers, Google has to embrace the idea (popularized by Scott Meyers but not original with him, probably due to Bertrand Meier) of API description as contract: you are making a contract with your developers that the API will perform as described. If it does not, it is a bug, and the sooner it is fixed, the better for a LOT of developers.

Thanks for your reply. It's definitely important that the Android (and other Google) APIs work reliably and consistently with how they're described. In fact, I'd say that's one of the key priorities for the framework team.

As far as the networking example, I think we're basically saying the same thing. Ultimately the app (and the OS) should be smart enough to provide a smooth interface irrespective of the connectivity currently available. The exponential back-off I suggested is part of that, but so is tracking your connectivity (and type), queuing any failed uploads, and adjusting your prefetching and network lookups based on the speed and reliability of your connection.

If the user doesn't want to connect to the network, they can disable that at the device level. They shouldn't be expected to make decisions like that within your app.