Random thoughts on API design

This excel­lent book by Steve Krug was a real eye open­er when I chanced upon it a few years ago.

image

I’m not a UI/UX design­er by trade or by train­ing, and as a back­end devel­op­er my appre­ci­a­tion for UI/UX has very lit­tle out­let in my day-to-day job either. But still I find plen­ty of sym­me­tries between UI/UX design and API/library design.

Don’t Make Me Think

When it comes to API design, I always cast my mind back to this talk by Joshua Bloch.

Slides

In this talk, Joshua out­lined a set of char­ac­ter­is­tics of a good API:

  1. easy to learn
  2. easy to use, even with­out doc­u­men­ta­tion
  3. hard to mis­use
  4. easy to read and main­tain code that uses it
  5. suf­fi­cient­ly pow­er­ful to sat­is­fy require­ments
  6. easy to evolve
  7. appro­pri­ate to audi­ence

Read­ing between the lines, the first 4 points essen­tial­ly boil down to a sim­ple rule – that we should appeal to human intu­ition, and close­ly resem­bles the usabil­i­ty study con­cept of affor­dance.

An affor­dance is a qual­ity of an object, or an envi­ron­ment, which allows an indi­vid­ual to per­form an action. For exam­ple, a knob affords twist­ing, and per­haps push­ing, whilst a cord affords pulling.

- Wikipedia

or in oth­er words, the eas­i­est and most obvi­ous way to do some­thing should also be the right way to do it.

Just as a great UI guides you towards your goal – be it buy­ing a flight tick­et or find­ing a hotel – a great API design should also guide you towards meet­ing your data needs.

How­ev­er, just as in the real world, Soft­ware is full of fail­ure exam­ples…

image

and some advices in Soft­ware are just as applic­a­ble in UX too.

Explic­it is bet­ter than implic­it.

Flat is bet­ter than nest­ed.

- Zen of Python

image

Just as UX experts can con­duct UX stud­ies by watch­ing users inter­act with a UI and track­ing their eye move­ments, etc. Per­haps as an indus­try, we should also con­duct usabil­i­ty stud­ies on our API offer­ings? We can mea­sure how quick­ly devel­op­ers are able to suc­cess­ful­ly per­form a set of tasks that require them to inter­act with the API in var­i­ous ways. For instance, how many attempts it takes them to cor­rect­ly retrieve the data they’re after.

Type Directed Development

In a sta­t­i­cal­ly typed lan­guage, good use of types goes a long way towards express­ing and com­mu­ni­cat­ing the assump­tions and lim­i­ta­tions we as API design­ers have made. Many func­tion­al lan­guages such as F# are very well suit­ed in this regard thanks to their pow­er type sys­tems.

For inspi­ra­tions on what’s pos­si­ble and how to apply them in prac­tice, see the fol­low­ing talks.

Scott Wlaschin on DDD with the F# type sys­tem

image

Edwin Brady – State, Side-effects and Com­mu­ni­ca­tion in Idris

Lamb­da Days 2015 — Edwin Brady — State, Side-effects and Com­mu­ni­ca­tion in Idris from Erlang Solu­tions on Vimeo.

Edwin Brady – Ver­i­fy­ing State­ful and Side-Effect­ing pro­grams using Depen­dent Types

Slides

Joshua also talked at length on a num­ber of prin­ci­ples you should fol­low to help you design a good API. Whilst the talk is based on Java, many of these prin­ci­ples would be famil­iar to any func­tion­al pro­gram­mer too.

  • API should do one thing and do it well
  • API should be as small as pos­si­ble but no small­er
  • Imple­men­ta­tion should not impact API
  • Min­i­mize acces­si­bil­i­ty of every­thing, max­i­mize info hid­ing
  • Min­i­mize muta­bil­i­ty
  • Sub­class only where it makes sense
  • Design and doc­u­ment for inher­i­tance or else pro­hib­it it
  • Don’t vio­late the prin­ci­ple of least aston­ish­ment
  • Fail fast – report error ASAP after they occur
  • Use appro­pri­ate para­me­ter and return types
    • use most spe­cif­ic pos­si­ble input para­me­ter type
    • don’t use strings if a bet­ter type exists
  • Use con­sis­tent para­me­ter order­ing across meth­ods
  • Avoid long para­me­ter lists
  • Avoid return val­ues that demand excep­tion­al pro­cess­ing
    • zero-length array, not null

Many oth­ers have said before that good OO is very sim­i­lar to FP, but the prob­lem remains that main­stream OO lan­guages such as C# and Java doesn’t do a good job in guid­ing you towards writ­ing good OO.

On Complexity

As soft­ware devel­op­ers, we’ve all been taught that main­tain­abil­i­ty is impor­tant but so often I find it dif­fi­cult to think about main­tain­abil­i­ty in clear, unam­bigu­ous ways since com­plex­i­ty itself is in the eye of the behold­er. We have invent­ed many tools and met­rics to help us mea­sure com­plex­i­ty, but none gives us a com­plete and accu­rate view of it.

Fur­ther­more, soft­ware engi­neer­ing is not only an engi­neer­ing activ­i­ty (in fact, Alan Kay argued that our stan­dards and prac­tices fall way short of those expect­ed of an engi­neer­ing dis­ci­pline) but also a social activ­i­ty.

Orga­ni­za­tions which design sys­tems … are con­strained to pro­duce designs which are copies of the com­mu­ni­ca­tion struc­tures of these orga­ni­za­tions

- M. Con­way

Adam Torn­hill also demon­strat­ed a num­ber of tech­niques that use our com­mit his­to­ries to unveil hid­den com­plex­i­ties that arise from the way peo­ple work togeth­er.

If you have depen­den­cy between soft­ware com­po­nents devel­oped by dif­fer­ent peo­ple, then you essen­tial­ly have depen­den­cy on peo­ple. Just as the com­mu­ni­ca­tion between soft­ware com­po­nents is a source of com­plex­i­ty, so too is the com­mu­ni­ca­tion between peo­ple respon­si­ble for these soft­ware com­po­nents.

Com­plex­i­ties can creep into your API in any num­ber of ways, e.g.

  • com­plex­i­ties with the under­ly­ing domain leak­ing through
  • imped­ance mis­match between the API’s designed use case and users’ actu­al needs

Hav­ing worked with no less than 25 of AWS’s APIs, we have encoun­tered a num­ber of such com­plex­i­ties. And since we are not in con­trol of the APIs them­selves, so we do the best we could to man­age and mit­i­gate these com­plex­i­ties with the use of DSLs.

Take a look at the fol­low­ing slides on some of these com­plex­i­ties and the approach­es we took.

Links