Skip Navigation LinksHome > View Post

Building your app version defense strategy – Part II

In Part I, I talked about building the smarts into your client to give you a chance to provide your users with some advice if you start to make breaking changes to your service, namely “upgrade please!”.

However, sometimes you don’t need to be quite so aggressive. In some cases you’re making a change to the server but would be happy to continue supporting older clients, they just might miss out on some new feature. I’m working on an upgrade to doto now which falls squarely in this category. Basically, I added timestamps to some records in the insert, a la:

function insert(item, user, request) {
    item.created = new Date();
    request.execute();
}

Easy enough (of course, the actual script is a little more complicated but I removed the unrelated guff to keep this post tidy). I built an offline capability in doto that syncs to Core Data to allow you to work with your data even when you’re disconnected from the internet (more on that in another post, it’s surprisingly simple). Now this broke my implementation in the older client because it now tries to save an additional column ‘created’ to my old Core Data store. CRASH. Oh well, the old client doesn’t need that information so I can just strip it out. However, the new client could take advantage of that data.

So the second key part of my versioning defense strategy is to pass information about the current client app version in every request. This way I can add code in my script to smartly remove properties that I know will break my client, e.g.

function insert(item, user, request) { 
    item.created = new Date(); 
    request.execute({
	    success : function() {
		    if (request.parameters.build < 1.2) {
			    delete item.created;
		    }
			request.respond();
	    }
	}); 
}

And on the read function

function read(query, user, request) {
    request.execute({
	    success: function(results) {
		    if (request.parameters.build < 1.2) {
			    results.forEach(function(r) { 
				    delete r.created;
			    }
			}
		    request.respond();
	    }
	});
}

To add this information to every request, I added another filter to my client (naturally) that encodes the additional data on to the URL.

Note – Ideally I’d have used headers for this but Mobile Services doesn’t currently allow you to control HTTP headers in scripts. If you’d like this feature too, get voting on our uservoice. I logged a feature request for this. My team is very customer led so this stuff does matter. We can’t make any promises about the order we’ll tackle things (there are usually multiple variables driving prioritization) but it really does count. Anyway, for now, some query strings on the URL will do just fine.

Here’s my Objective-C filter to add some information about the app to each and every request.

In this case, I add three query string values:

  1. build – this is the bundle’s CFBundleShortVersionString
  2. version – this is the bundle’s CFBundleVersion
  3. p – this indicates the platform (e.g. iOS)

You may want to add more information like OSVersion and maybe device specific information but I’ll leave that as an exercise for the reader.

My last post incited a few great comments and questions - I look forward to more of that with this post. In one case, Nate noted how it's hard to ship breaking changes as you don't know exactly when your app will land in the store. If you genuinely have to break old clients (and the above, backwards compatible approach won't work for you) AND you can't be sure when you can roll your service forward then you’ll need to use a combination of the patterns in this series. That is, your server effectively supports both modes of operation for some time (switching behavior based on the version passed in the querystring). Then, once your new version has bedded in, you can send out those upgrade messages!

In reality I believe we tend to spread our changes over a number of ‘bridging’ releases where in addition to some tolerance in the server, a new client also has a degree of tolerance with respect to server features ‘lighting up’; this helps to pave over these cracks. Some combination of all of the above is applied based on the specific scenarios.

Of course, a key thing to avoid is supporting many versions with significantly different logic and schemas if at all possible.

Versioning is challenging, and there is no silver bullet. But if you loosely follow the advice in this post and Part I (whether you use Mobile Services or not), you’ll be in a much better place to handle those challenges.

 
Josh Post By Josh Twist
3:06 AM
18 Mar 2013

» Next Post: In-App Purchase and Mobile Services.
« Previous Post: Building your app version defense strategy &ndash; Part I

Comments are closed for this post.

Posted by Nate Jackson @ 18 Mar 2013 3:53 PM
Another great post Josh. I think having access to the http headers is a good feature of the platform, but I believe Versioning the client/server should be baked in a bit more. I think your UserVoice feature request at http://mobileservices.uservoice.com/forums/182281-feature-requests/suggestions/3323126-api-versioning is more on point.

Posted by Ignacio Fuentes @ 18 Mar 2013 4:29 PM
"allow you to work with your data even when you’re disconnected from the internet (more on that in another post, it’s surprisingly simple)."
Wow, what a tease ;)
Great stuff Josh. Keep em coming.

© 2005 - 2014 Josh Twist - All Rights Reserved.