
API Design - Matt Gemmell - protomyth
http://mattgemmell.com/2012/05/24/api-design/
======
timtadh
This is a great document. Lots of detail, good recommendations. I recently
gave a short lecture on API design and came up with the following
"touchpoints" of API design. These are sort of general guidelines and a lot
less specific than the article was.

 _orthologonal_ \- Properties and methods should not overlap in functionality.
If two methods do sort of the same thing but differently that is a design
issue. They should either do entirely different things or there should only be
one method. Providing two is confusing and can potentially lead to bugs.

 _consistent_ \- Naming of methods, properties and other entities should be
consistent. Example if you have a method "getColor" you should not have a
method "get_width" or "width" or any other variation. You should stick with
one format. In the same way argument orders should be consistent. For example,
if you have a method "translateCoords(x, y)" you should not have a method
"setTopRight(y, x)". This principle is extensible into other domains as well
(such as exceptions).

 _composable_ \- Methods and functions should be reasonably composable.
Instead of creating giant methods which do many things create small methods
which do one thing really well. Then provide a clear way to compose methods
together in order to accomplish a task. In this way methods do not have to be
written which explicitly perform complex tasks.

 _learnable_ \- All design is for naught if the API is not well documented.
Much has been written on the importance of this step but it should be stressed
again. Good documentation can make up for any number of short comings in the
design. So document your api, provide examples of how to accomplish tasks.

 _discoverable_ \- Hand in hand with documentation comes the ability to be
able to naturally discover other parts of an API. This can be provided in a
number of ways based on language and platform. In Python providing great
dynamic help via the "help()" command and docstrings is a good start. For web
APIs consider providing an "explain" API which returns documentation.

~~~
wcarss
I believe that the word _orthologonal_ should read _orthogonal_.

I don't mean to downplay your points; I agree wholeheartedly with your post,
but I actually did a double take and thought I might have been spelling the
word incorrectly for some time.

~~~
timtadh
Absolutely. That was a mistake I would fix it but I no longer can.

------
ebun
"APIs are UX for developers."

This simple statement just changed the way that I think about API's.

~~~
drawkbox
I literally paused reading when I read this simply put exact statement. I try
to focus on minimal, simple apis and preach it to developers and clients for
quick integration and maintenance. Now I have this powerful statement to save
hours of explaining mainly to non technical people. I have always thought this
but never so crisp and to the point. We need t-shirts. Doing a quick search of
Google, it appears a few others have recently stated this as much but it is an
idea that should take hold: <http://www.kryogenix.org/code/apis-like/>

~~~
mmahemoff
I don't think the topic is new per se, as really a lot of software engineering
history has been about dealing with the human aspects of development. Even
fundamental concepts like object-orientation are grounded in psychology and
even before software, there were precursors for this kind of thinking in the
way machines and working environments were designed. Josh Bloch's Effective
Java is a more recent example.

The difference now is that UX has become a popular, well-understood,
discipline. So we have a lot more concepts to take back into API design.

Some of us have been capturing relevant links on the topic on a Developer
Experience page (<https://plus.google.com/116834904360889286443/posts>) and
under the #devexp hashtag. We don't have t-shirts, but we do have a logo in
need of much love! I'll go add a link to that presentation too.

------
sambe
Desirable qualities

    
    
        Intuitive
        Forgiving
        Frictionless
    

Most of this article applies equally well to humans...

~~~
zdouglas
+1 (Funny.)

------
joe_the_user
Seems like decent advice but perhaps pretty library specific.

The only thing I'd argue with is: "Use semantic objects for parameters".

I haven't had any trouble passing a null pointer and casting it. Your API is
going to be expanding regularly and the more message-passing objects you
create, the more message passing objects' definitions you'll have to expand as
the API expands.

~~~
saddino
The best approach I've seen for expanding APIs in iOS is the one Apple tends
to use: pass a single NSDictionary of keyed parameter values. The API
parameter list never has to change, it's guaranteed to be backwards compatible
and you can still enforce use of semantic objects as the allowed values for
the new keys you are adding.

(It just struck me that this mirrors the approach Apple used often in Mac
Toolbox: passing parameter structs with a version field, instead of modifying
the API parameter list itself).

------
zby
A propos 'intuitiveness' and learnability - here is a great anecdote from
'Making Software', and my interpretation of it:
[http://perlalchemy.blogspot.com/2012/05/on-importance-of-
int...](http://perlalchemy.blogspot.com/2012/05/on-importance-of-intuitive-
names.html)

------
tumultco
This is a comprehensive list of many considerations. Top of the list should be
to have as small of an API as possible. API is forever. If you expose too much
up-front then it increases the surface area of getting things wrong, which
developers will be stuck with for a long time or need to deal with eventual
deprecations which is fun for no one. Also, more API leads to higher
complexity in learning. It is better to see how your API is being used and
slowly reveal more functionality as you become confident it is what developers
need.

I also believe API should go through a feedback/review processes with peers.
Other developers will have used different patterns, have a different base of
experience with the platform, and can generally help find common mistakes. So,
here's some of my feedback/nitpicks on MGTileMenuController.h :) --

    
    
      @property (nonatomic, weak, readonly) id<MGTileMenuDelegate> delegate; // must be specified via initializer method.
      - (id)initWithDelegate:(id<MGTileMenuDelegate>)theDelegate;
    

It is very rare for delegates to be required for initialization, and I don't
see why the @property for the delegate needs to be readonly? Perhaps an owner
would be a better model?

The isVisible property looks like this:

    
    
      @property (nonatomic, readonly) BOOL isVisible;
    

But typically for BOOLs the property is the normal form with a specific "is"
getter:

    
    
      @property(nonatomic,readonly,getter=isVisible) BOOL visible;
    
      // N.B. All of the following properties should be set BEFORE displaying the menu.
    

This makes me wonder what happens if I want to change them later? It seems
that they should always be able to be changed, and in general comments
shouldn't be required to understand library behavior as they'll just be missed
by developers anyways.

    
    
      - (NSInteger)nextPageNumber:(NSInteger)currentPageNumber; // zero-based pageNumber
    

Why would the nextPageNumber not be +1 from currentPageNumber? Also, why do I
need to supply a currentPageNumber when it appears currentPage is a @property
of the class? Is there a difference between "currentPage" and
"currentPageNumber" as their naming indicates there might be one? Also, it
might be better to name the method similar to the NSIndexSet convention
"indexGreaterThanIndex." Should an NSIndexSet actually be used instead?

    
    
      - (UIBezierPath *)_bezelPath;
    

This method looks like it is private/internal because of a leading underscore,
so it shouldn't be exposed in the header. Also, Apple has reserved naming
methods with a leading underscore (especially important when you are
subclassing!).
[https://developer.apple.com/library/mac/#documentation/Cocoa...](https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CodingGuidelines/Articles/NamingMethods.html)

In general, if the developer doesn't need the utilities, I highly question
adding them to the header since you'll be responsible for them for all time.

Finally, why not add the delegate protocol to the header file so everything is
consolidated for the developer using the class?

~~~
tmurray
"API is forever. If you expose too much up-front then it increases the surface
area of getting things wrong, which developers will be stuck with for a long
time or need to deal with eventual deprecations which is fun for no one. Also,
more API leads to higher complexity in learning. It is better to see how your
API is being used and slowly reveal more functionality as you become confident
it is what developers need."

This is so, so true. If you find yourself thinking about exposing something
because someone might have a use case for it or to provide greater control
over some very specific corner case, write it down, file it somewhere, and
revisit that when you see how people are actually using your API. Don't
mistake all the ways your API could be used for all the ways people want to
use it.

------
conradev
This is a fantastic article. I'm glad to see that I have picked up some of
these rules intuitively, from reading others' code, and studying the design of
Apple's APIs. I do have a couple questions though, that hopefully someone can
answer.

With the introduction of blocks in iOS 4, some APIs have shifted to using
blocks for messaging. What is the general opinion on this?

Also, for components that benefit from an internal state machine - which is
best practice to notify objects of a change in state? Delegate messaging,
notifications, or just KVO?

~~~
pirateking
I enjoy using blocks in provided API, as well as writing some of my own
private API with them. The code locality is very nice for many things like
enumeration and animation.

As for notifying objects of state changes, all three methods are used. I use
NSNotificationCenter if there will be multiple objects who may need to know
about it, KVO as glue for the model and the view, and delegate messaging if
there is only one object to notify.

------
smattiso
This is great advice for anybody who does iOS development. If you are creating
a component that is only going to be used internally in your application, it's
tempting to just slap it together without really thinking about it. In the
long run this really will save you time and headaches.

------
eblackburn
I like this. It espouses good engineering principles (tell don't ask etc.) the
advice here is in my opinion applicable to most platforms and domains.

------
rys
Rule #4 is a nonsense. Why should the data you pass to a class to initialise
it be publicly accessible on the class afterwards as a matter of course?

~~~
billjings
This is one of my favorite points of the writeup, because I've been bitten by
it repeatedly over the past year.

 _Not_ providing a getter for that data means that if the client wants to use
it elsewhere, it needs a second home. So now I'm keeping track of not just a
HNCommentsView, but also an object holding the HNText and HNUser it's
displaying.

Any time an API doesn't expose that data, there's an implicit assumption of,
"The person using this class won't (or shouldn't) need to get at this data
this way, so I can leave it out." Which makes it extra irritating to me when
it's not there and I need it.

~~~
makecheck
Like many things it's _generally_ useful but _not_ a law that applies to every
class.

Some counterexamples that come to mind...

\- [NSImage initWithPasteboard:] (must NSImage retain the original pasteboard
and/or its contents for all eternity, in case you want it back, even if it was
in some odd format like NSPICTImageRep that probably doesn't match the
internal representation?)

\- [NSDictionary initWithContentsOfFile:] (must NSDictionary remember the file
path for eternity, and/or its contents, in case you want it back?)

Yes, if an initializer is basically accepting things _that become properties
anyway_ , it makes sense to expose those properties. But initializer arguments
are not necessarily sensible or useful to keep around in all situations.

------
lucian1900
> plenty of the Steve Jobs type of intuitive, delightful, empowering magic

Huh?

