Tue, 18 Mar 2008

It's an elementary goal of API design to make something easy to
use: easy for yourself, easy for yourself next year, easy for others.
Let's take that as a given.

Many goals will conflict with "easy to use", but the subtlest is
the requirement that an API be hard to misuse. Ease of use
attracts users, but difficulty of misuse keeps them alive.

To make this concept crisp, I have two real life examples. The first is the safety catch on a gun. Hard to misuse beats easy to use.

The second example is the Linux kernel's kmalloc
dynamic memory allocation function. It takes two arguments: a size and a flag. The most commonly used flag arguments are GFP_KERNEL and GFP_ATOMIC: I'll ignore the others for this example.

This flag indicates what the allocator should do when no memory is
immediately available: should it wait (sleep) while memory is freed or
swapped out (GFP_KERNEL), or should it return NULL immediately
(GFP_ATOMIC). And this flag is entirely redundant:
kmalloc() itself can figure out whether it is able to sleep
or not. Implementing malloc() would be a no-brainer, and kernel
coders generally like ease of use. So why don't we? [Correction:Jon Corbet
points out that it's not entirely redundant in some configurations; we'd
need to do a few lines extra work.]

Because atomic allocations should be avoided: they're drawing from
a limited pool and more likely to fail or make other atomic
allocations fail. By placing the burden of specifying this onto the
author, we make atomic allocations easier to spot and thus harder to
abuse.

And if we want to make our APIs harder to misuse we need to
measure how an API scores, and that'll be the topic of the next post.