Friday, 21 December 2012

MSDN and MS Fail - Developing Store Apps? You're on your own

One of the reasons I often say to people that the Microsoft stack is the best to work on is the quality of the documentation.  For most APIs you get decent documentation as to what the method/class does, what kind of parameters to pass and, most crucially, why the operation will throw an exception (and what to do about it).  Across most of the BCL, too, you get lots of helpful exceptions when things go wrong - so you can address any issues and try again.

For this post I'm going to be working primarily with a random pair of examples (and it's by no means the best, especially throughout the Windows Store MSDN you'll see stuff like I'm about to show all over the place):

From the BCL MSDN: The AesManaged class (from the core BCL) and the documentation for it's CreateEncryptor(byte[], byte[]) method.

With those two opened in tabs, let's now open the Windows Store MSDN topics DataProtectionProvider and the DataProtectionProvider.ProtectStreamAsync(IInputStream, IOutputStream) method.

Font Size and Page Width

The first thing to note between the two different types of documentation is that whilst both are fixed width, the BCL pages are slightly wider.  'Why is that a problem?'  You ask - just look at the size of that text on the Windows Store pages!  The following image shows what the class name code block looks like in my browser (first) and what it would look like if it used the same font size as the BCL documentation:

image

It's about 55px difference in width there, for less than 50 characters.  So consider that for the code examples this means a lot more broken lines and wrapping: (screenshots from the DataProtectionProvider topic):

image

image

That second one is almost laughable - actually (and I'll be getting to this in a minute).

Now of course MSDN also does have a general design rule that code samples shouldn't wrap.  The fact that these do means someone in the QA team there either should be given a stern talking to - or the sack.

But of course this gripe doesn't just stand for code samples - the text in general is all larger.  The difference is that the BCL stylesheet uses 12px for the font size, and the new one uses 0.87em.  Okay so using ems is better; but you should try to get it right.  Sure I can change my font size to 'smaller' in IE and get the desired effect - but I just shouldn't have to.  At least start with an em value that is equivalent, for most people, to what the original MSDN uses - I believe 0.75em is a good one for most cases.

The overall effect of the larger text, in my opinion is to make it all look unprofessional and makes me feel like I'm working with a toy framework.

The page size has been reduced to 985px vs 1220px.  So on my 1680x1050 desktop (I'm sure a minimum for most developers) I end up with a preponderance of whitespace in the gutters of my browser.  Okay so it stops people with 1024px screens having to pan - that's great.  But for people with bigger screens it just looks crap.  This is what the min-width CSS rule is for, people!  We obviously don't want our lines too long (and re-pagination of code samples would be a step too far and too-long lines of text get hard to read), so a liquid layout that grows to a max of 1220px would seem to make the most sense here.

On to the content - the code samples

Why oh why oh why are the code samples for the DataProtectionProvider, which are written in C#, not highlighted, and why oh why does it say in the language tag tab on the top: 'None'.  I assume the two are related, but either way it's immensely difficult to read those walls of code.  In fact, because they're not coloured they're actually just walls of fixed-width text, which is horrible.  It's also especially difficult when we have seriously ugly code wrapping as shown in the screenshots above.

Clearly, whoever wrote the samples was expecting the available width to be the same as it is for the 'proper' MSDN - but nobody in the design department told him or her that it isn't.  Left-hand, right-hand…

Also - where are the samples for other developers?  Now in some other classes and APIs you will find multiple-language examples, but you'll also find a lot like this, where only one language is presented.  Not acceptable.

In nearly all of the .Net BCL documentation you'll get at least C# and VB; and sometimes JScript.

The runtime experience - and the actual documentation

This is where the real problems start.

First - and we're looking at the ProtectStreamAsync method documentation here - a reference is made back to which constructor you should use.  That constructor takes a magic string.  Now try and find a list of all the magic strings that are acceptable for that constructor.  Go on - I dare you - you simply won't.

In fact, if you run the Win RT Crypto API sample with either of the WEBCREDENTIALS versions - which do, admittedly appear to be placeholder strings for you to fill in - the sample crashes!

And this is where we get to what is actually my biggest gripe - and this gripe is not just with the documentation, but the whole WinRT .Net layer too:  Exceptions.

Knowing what exceptions are to be expected if something goes wrong is a central tenet to .Net framework programming.  This method's documentation contains absolutely nothing about the exceptions that will be raised if something goes wrong.  The implication being that nothing can go wrong if no exceptions are listed.  Not so!  As my previous paragraph states - the sample crashes with this exception:

image

To find out what that actually means I had to google 'dataprotectionprovider hresult 80090034' (but notice google changes the dataprotectionprovider name to 'data protection provider' - because if you actually search for the original phrase you get no results).  The top result here is COM Error Codes which lists 0x80090034 as 'NTE_ENCRYPTION_FAILURE: Encryption Failed'.

Marvellous - thanks for that.

So what should change?

Well the COM error code list is absolutely fine - I have no actual gripe with that; if I'm doing native development then I do need to know what all these error codes are.

But I'm not doing native development, I'm doing .Net development, which uses exceptions to communicate failure.  So that exception should be raised with some meaningful information about what's actually gone wrong (based on the HRESULT that was returned) and/or - MSDN should contain a good list of the exceptions that can occur and why they will occur.  In this case, there's clearly something wrong with the magic string I sent to the DataProtectionProvider constructor.  But since I've got no way of knowing what the acceptable values actually are; and I've got no way of knowing what's actually wrong with the string I did send - the API is basically useless to me.  It really might as well not exist at all.

 

In fact this whole exceptions thing is something that's pervasive throughout the 'new' MSDN, and I think something that points to a core decision that's been made somewhere.

In Windows Store/RT development, exceptions apparently never happen and if they do, then you should swallow them - don't worry about what they actually are.  I came across a code sample on a topic the other day (I'm sorry but I can't remember what it was) which used a catch(Exception){ } block to swallow every exception and return null from the example method.

There wasn't even a paragraph saying something like 'this is not good practise' and linking off to a helpful topic about handling exceptions effectively (like this very topic taken from MSDN itself!).

I mean: wow.

So what's this framework and it's documentation trying to achieve?

…Because I really have found it to be less than effective in many many scenarios so I can't imagine it's to be helpful.

As it stands, in my opinion, it's to make Windows Store development accessible to the beginner programmer, that's what.  The aim is to get as many people blundering about in the new .Net framework; hiding all the complicated stuff like what could go wrong and why, so that more store apps are developed.

Okay so that's a reasonable goal, but at the same time it's alienating people like me who want to know why stuff goes wrong and what I can do to either fix it or avoid it.  I really wanted to use that DataProtectionProvider, for example, so I could use out-of-the-box encryption tied to the user's roaming identity (I'm assuming that the "LOCAL=User" magic string therefore isn't applicable, but I wouldn't bloody well know would I!?) to store some sensitive data that I also wanted to roam.  But I can't - because I literally have no way of knowing how to do it.

Oh and there's another target audience implicit within this - Java and Objective-C developers from Android and iOS-land.  They might be just starting to learn .Net and C#/VB - so let's not clutter up our documentation with loads of technical details that might frighten them off (I'm not implying that such devs are non-technical by the way - I'm suggesting that the MSDN team is condescending to them) and pretend that all our APIs are really friendly and will always work.

They'll really thank you when shizzle does happen but there's no fizzling clue as to what's gone wrong.  Really, they will.  If I, a professional .Net developer with many years experience across most of the .Net envronment stacks (service, desktop, command line, web forms, mvc) am getting really frustrated with this then I'm sure they're going to feel absolutely abandoned.

And finally - another example - the Geolocator

My last rant - and another indicator of both a documentation fail but also some shortcomings in WinRT itself - is one of the very very core APIs that you'll use in Windows Store Development: the GeoLocator class and it's GetGeopositionAsync overload.  (Note here, for some reason, all the code examples are now magically in JavaScript and not available in anything else!).

Now - for some reason I've yet to fathom.  Geolocation will not work on my desktop in the office.  Any app that tries to use my location will never get it.  Even IE10 will not dish out a location (and yet IE9 running on Windows 7 would happily get one resolved by IP address - figure that one out).  Now, I'm thinking this likely because I'm in an enterprise environment with a corporate proxy/firewall - but really the reason it doesn't work is irrelevant to this discussion.  As a developer I should be able to handle all scenarios effectively and easily - especially when the API and it's documentation implies that all the issues are already dealt with for me.

So, anyway, JobServe Windows Store App I'm developing right now will of course take advantage of geo-location if it's available - so I wrote this very simple piece of code, in an async method:

var loc = new Geolocator();
var t =
    await loc.GetGeopositionAsync(TimeSpan.FromDays(7), TimeSpan.FromSeconds(10));

That means - 'get a location that's at most 7 days old, and return within 10 seconds if you can't'.

On my machine, with whatever geo location issues it has, this call - never returns - not after 10 seconds, not after 5 minutes.  I left it running for an entire lunchtime once - still nothing.

The timeout value expressed here implies that you can control just how long to wait for a location update to be received.  In fact that only seems to work if the Geolocator can be initialised (on my machine it never moves past the 'Initialising' state). 

But surely the documentation will tell me about this - and how to handle it.  No - it doesn't.  It mentions about 7 second timeouts when the object is in 'Connected Standby' - but that doesn't even seem to relate to anything else on the Geolocator object - it's certainly not one of the enum values in the PositionStatus enum.

So what can I do - our app will be used by people in business environments to search for jobs in their lunch breaks (contractors especially) - I'll have no choice but to do a manual wait for the ten seconds and move on manually if the task hasn't completed.  The task can't be killed or terminated, though, because it's an IAsyncOperation and doesn't support CancellationTokens - so I'll just have to leak the task until the app is restarted.

All in all - that's not good - and to be honest I'm embarrassed to have to deploy code to people's machines that will do such a thing.

But I have no choice, because for the first time in a long time, Microsoft have dropped quite a few balls with WinRT and it's .Net interface - and all the time they remain rolling around the floor I'm finding the experience of developing for it a bit like swimming through treacle.

I should say, though, the WPF element is fantastic - it's a really mature environment now - and plugging into that has been a joy.  Documentation, though - again - a bit light (I keep having to go to the full framework's documentation to get decent XAML examples for some of the stuff that isn't new to RT).

2 comments:

  1. Thanks for this article! I totally agree with you.

    ReplyDelete
  2. I am having the same problem with GetGeopositionAsync() as well. But it does work for me sometimes, randomly, making it very unreliable. I'm stuck there and I am not able to find any alternatives. I want to get the client's location for a weather app I'm developing. My first app and this is not a good start. I really really need to get the user's position to display accurate weather. Any ideas most welcome. It's been almost a week looking to solve this issue.

    ReplyDelete