IanG on Tap

Ian Griffiths in Weblog Form (RSS 2.0)

Blog Navigation

April (2018)

(1 item)

August (2014)

(1 item)

July (2014)

(5 items)

April (2014)

(1 item)

March (2014)

(1 item)

January (2014)

(2 items)

November (2013)

(2 items)

July (2013)

(4 items)

April (2013)

(1 item)

February (2013)

(6 items)

September (2011)

(2 items)

November (2010)

(4 items)

September (2010)

(1 item)

August (2010)

(4 items)

July (2010)

(2 items)

September (2009)

(1 item)

June (2009)

(1 item)

April (2009)

(1 item)

November (2008)

(1 item)

October (2008)

(1 item)

September (2008)

(1 item)

July (2008)

(1 item)

June (2008)

(1 item)

May (2008)

(2 items)

April (2008)

(2 items)

March (2008)

(5 items)

January (2008)

(3 items)

December (2007)

(1 item)

November (2007)

(1 item)

October (2007)

(1 item)

September (2007)

(3 items)

August (2007)

(1 item)

July (2007)

(1 item)

June (2007)

(2 items)

May (2007)

(8 items)

April (2007)

(2 items)

March (2007)

(7 items)

February (2007)

(2 items)

January (2007)

(2 items)

November (2006)

(1 item)

October (2006)

(2 items)

September (2006)

(1 item)

June (2006)

(2 items)

May (2006)

(4 items)

April (2006)

(1 item)

March (2006)

(5 items)

January (2006)

(1 item)

December (2005)

(3 items)

November (2005)

(2 items)

October (2005)

(2 items)

September (2005)

(8 items)

August (2005)

(7 items)

June (2005)

(3 items)

May (2005)

(7 items)

April (2005)

(6 items)

March (2005)

(1 item)

February (2005)

(2 items)

January (2005)

(5 items)

December (2004)

(5 items)

November (2004)

(7 items)

October (2004)

(3 items)

September (2004)

(7 items)

August (2004)

(16 items)

July (2004)

(10 items)

June (2004)

(27 items)

May (2004)

(15 items)

April (2004)

(15 items)

March (2004)

(13 items)

February (2004)

(16 items)

January (2004)

(15 items)

Blog Home

RSS 2.0

Writing

Programming C# 5.0

Programming WPF

Other Sites

Interact Software

Defaults and API Design

Tuesday 11 May, 2004, 01:12 PM

Once I had got to the bottom of a problem I was having recently with the CryptoAPI, I realised that I may have become accustomed to trusting default values a little too much.

One of the reasons I prefer using the .NET Framework to Win32 is that .NET APIs tend not to require me to pass in great long sets of parameters - they usually provide simple overloads that supply sensible defaults. So when I have to return to Win32, I am now too ready to pass in NULL whenever I'm allowed to. This was how I came to make the mistake of allowing the CryptoAPI to choose a default key length for me, as described in the entry linked to above. (The key length/effective key length schism is an entirely different matter - that's just an unhelpful API quirk; I'm glad it has been fixed in XP/2003 even though this change was what caused the problem to manifest itself.)

But this got me thinking about the wisdom of relying on default values. If it's going to get me into trouble like this, perhaps I shouldn't be so ready do to it.

But why does the Win32 Crypto API offer to supply defaults if the resulting behaviour is going to be inconsistent in different platforms, or even in different environments on the same platform? By allowing the CryptoAPI to pick default values, I am saying 'Use whatever the local settings are; I am aware that what I encrypt with these settings might fail to decrypt in a different environment'. Once you understand that this is the implication of using the defaults, the behaviour you see makes sense. But this is not necessarily what developers will expect - I tend to assume that if I can ask for a default to be supplied for me, the default will be benign. In this case, it wasn't so I got a nasty surprise.

If defaults might not make sense, then I don't think that they should be defaults. If the system cannot determine non-surprising defaults for me in a safe way, then I should be required to set such parameters explicitly. In my CryptoAPI example, it would have been better if the key generation APIs simply failed when given a key length of zero, rather than picking a context-sensitive default. If you want to use a potentially non-portable environmental setting, that should require an explicit step, rather than being a default. For example, rather than passing in NULL to get the default crypto service provider, it would be better for there to be a special value you can pass in to indicate that you want whatever the local environment's default provider is - that way you are opting into environmentally-sensitive behaviour.

I realise that there's a tension here. Simplicity is a desirable property of an API, and one of the ways to make an API simple is to have it provide intelligent defaults. But I think it's important for defaults to be non-surprising. This is especially important for .NET APIs, where you might not even see any evidence that some default value or behaviour has been selected on your behalf. With Win32 APIs, passing in a value of zero or NULL acts as a cue that something might be decided on your behalf. (Although I came unstuck with the key length thing because the key length is squished into the top 16 bits of a flags parameter, rather than getting its own parameter...) With .NET, you don't get this cue. An API with optional defaultable inputs tends to offer a range of overloaded constructors and properties allowing values to be set in scenarios where you don't want the defaults. This means that it's not remotely obvious from the code that the value for which a default is being supplied even exists. Consider this example:

StringFormat sf = new StringFormat();

How many parameters just got set to default values for me there? It's really hard to tell. In fact one of the APIs that accepts a StringFormat object is Graphics.DrawString, and several of its overloads don't even take a StringFormat, so it wouldn't necessarily occur to a developer to ask the question in the first place. Drawing text is a surprisingly complex endeavour, yet the APIs can make it look simple by providing sensible defaults for the myriad tweakable settings. In this case, this is A Good Thing, because the defaults chosen are in fact what you want most of the time. Just as well really, since if you're new to GDI+, you might not realise just how much is being done on your behalf when you see this:

protected override void OnPaint(PaintEventArgs e)
{
    e.Graphics.DrawString("Hello, world", Font, Brushes.Black, 0, 0);
    base.OnPaint (e);
}

So in .NET APIs, I think the principle of least astonishment is even more important than it is in C-style APIs like Win32. As long as APIs get this right, the code that uses those APIs can remain clean and uncluttered. (And of course, our code can provide explicit values when non-default behaviour is required.) But if the defaults have the capacity to surprise, then we're in a world of pain.

Copyright © 2002-2024, Interact Software Ltd. Content by Ian Griffiths. Please direct all Web site inquiries to webmaster@interact-sw.co.uk