March 2009

Volume 24 Number 03

{ End Bracket } - Perfect API Design

By James Waletzky | March 2009

Why is good Application Programming Interface (API) design so difficult? Just throw together a few classes with a few basic methods on them and call it done.

You could do this, if you don't mind other developers cursing you—and referencing your API as an anti-pattern. How can this be avoided? Aim for perfection with API design. Yes, perfection.

Designing an API is similar to designing a user interface. Both are entry points into an application and the first thing a user (or developer) interacts with. Picture your favorite consumer electronics device in conjunction with its competitors. Mine is my beloved TiVo digital video recorder. What makes the TiVo UI great?

  • Usage is simple
  • Misuse is difficult
  • TiVo does one thing well
  • It does not do anything unexpected
  • It has never crashed in multiple years of usage
  • Actions/tasks are intuitive and easy to discover
  • The UI is consistent, polished and perfect, or close to it

The same principles hold true with API design. Let's pick on one of my favorite API anti-patterns from the COM world: IOleCommandTarget::Exec. This API is typically used to enable some kind of object, and the container that it sits in, to dispatch commands to each other.

An example implementation is a COM control that communicates with its container to modify menu items and toolbars. This API is a slightly stronger-typed version of a command interface that takes in a generic string dictating what the implementation is supposed to do.

Here is the IOleCommandTarget::Exec API signature:

HRESULT Exec( const GUID *pguidCmdGroup, // Pointer to command group DWORD nCmdID, // Identifier of command to execute DWORD nCmdExecOpt, // Options for executing the command VARIANTARG *pvaIn, // Pointer to input arguments VARIANTARG *pvaOut // Pointer to command output );

Let's analyze this API according to the principles just discussed.

Is the API Intuitive and Discoverable? Is this the first place a developer would look to modify a toolbar? Likely it's not, unless you have the tribal knowledge about this API. Even a search of MSDN documentation would end in frustration.

Is It Simple? The API takes five parameters that are generically named. Documentation is required to thoroughly understand the usage of each parameter. A simple API exhibits intention and clarity just by the name of the object (for context) and the name of the method.

Is the API Strongly Typed and Difficult to Misuse? No. The variants are not strongly typed and the execution options are not enumerations, but generic DWORDs. Strongly typed parameters help find errors at compile time instead of at run time.

Is the API Cohesive? An implementation of this function typically has many responsibilities, not just one. A well-named API for one specific purpose (e.g., AddToolbarButton) is much more cohesive. An implementation of the Exec method likely switches on the command ID and has different actions based on this condition.

Is the API Free of Side Effects? Hard to say, but an implementation of the API could do practically anything due to its lack of cohesion.

Is It Reliable? The famous design principle "design to interfaces" helps enforce testability on an object. This API is inherently testable, but the number of test cases to verify may be huge due to its lack of cohesion. Reliability is at risk.

Is It Consistent? This API does have some consistency in that it returns an HRESULT and uses GUIDs to identify sets of information. However, it is difficult to judge consistency without knowing how poorly designed the rest of the interfaces on an object are.

Anyone learning to use IOleCommandTarget::Exec likely needs instruction about its usage from a peer or a book. The best APIs are simple and usable without documentation, although documentation may still be required to understand details such as error handling.

In conclusion, your APIs and the APIs you review should be as easy to use as TiVo. A highly discoverable, strongly typed, cohesive, side-effect free, reliable, consistent, and polished API makes your developers' lives easier by increasing satisfaction and decreasing support costs. Many interfaces in the Microsoft .NET Framework are great examples. To achieve all of these design gains, aim for perfection when designing or reviewing any API design.

James Waletzky is Senior Development Lead in the Consumer Internet and Developer Experience team in Windows Mobile. In addition to leading a product development team, James is passionate about improving engineering practices at Microsoft.