The Situationally Aware Algorithm

Mountain lion lounging in a cottonwood tree during the heat of the day.

Developers often pick an algorithm for its speed, but sometimes it makes more sense to dynamically pick an algorithm based upon the resources available at a particular moment. For example– the “Update All” feature for installing available updates for applications on Android Lollipop devices. “Update All” will download and unpack available updates one after the other, without waiting for the first update it unpacked to be installed. This parallel work queue approach makes sense from a time-saving perspective . . . until you try doing it on a phone with only 400-500 MB of storage space.

When storage space is limited, the processes of downloading the next update and unpacking the last downloaded update rapidly fill up the storage, usually faster than the installing process can use and dispatch the files. Suddenly, you have multiple update error messages in your notifications due to “insufficient space.”

Now, our annoyed Android user has a choice:

  1. Clear their cached data in “Settings -> Storage” and hope this frees up enough space for the “Update All” feature to work properly.
  2. Update each Android app one at a time.

But what if the “Update All” process checked the available storage space before it started downloading updates, and based upon a particular threshold, decided between downloading all the available updates in rapid succession or downloading the first available update and waiting for it to be updated and purged before proceeding with the next download?

The idea of a “situationally aware algorithm” is not new. We can dynamically apply different sorting algorithms to data by determining the size of the dataset beforehand. We can determine how much free space remains in storage on an Android device by using the getFreeSpace() method. We have the means to inspect the resources available to us before we decide to tackle a particular problem, and yet more often than not we plunge ahead with a predetermined “one size fits all” approach.

I blame satisficing. I’m also guilty of it.

When I walk in the woods in my neighborhood, I never look up to see if a mountain lion might be perched on a limb I’m about to pass under. Mountain lions are a non-issue for me, and walking beneath tree branches is an experience which I take for granted will be uneventful.

Until, of course, it isn’t. 😉

Similarly, if we apply an algorithm for sorting large datasets to a smaller dataset, it will still give you results. It’s only when we come across a dataset which completely breaks the algorithm and get no results back (e.g. the dataset is so large it consumes all available RAM) we begin to question if the currently implemented approach to the problem is the correct one.

On the bright side, I think the adoption of practices like User Stories and Test Driven Development– particularly in team approaches where the stories, tests and code are written by different people– is gradually pushing development beyond the satisficing “just make it work” mentality into a more nuanced, situationally aware paradigm.

In the meantime, the best we can do is keep learning our craft, be patient with the status quo, and . . . start checking tree branches for mountain lions before we walk under them.

Make It Trivial

“It makes no sense to try, or even want, to fit into a place where you don’t belong . . . My advice to geeks? If you don’t like it, leave, leave fast, make it trivial. Come to terms with who you are.”

— Jesse Dailey
from Jon Katz’s book, “Geeks”