Friday, August 9, 2013

Characteristics of a Good API

How to Design a Good API and Why it Matters
Joshua Bloch
Principal Software Engineer


Easy to learn
Easy to use, even without documentation
Hard to misuse
Easy to read and maintain code that uses it
Sufficiently powerful to satisfy requirements
Easy to extend
Appropriate to audience

 Your job is to extract true requirements  Should take the form of use-cases

USE-cases
Can be easier and more rewarding to build something more general


Start with Short Spec 1 page is Ideal

At this stage, agility trumps over completeness

Bounce spec off as many people as possible
_ Listen to their input and take it seriously

If you keep the spec short, it’s easy to modify

Flesh it out as you gain confidence
_ This necessarily involves coding

Write to Your API Early and Often

Start before you've implemented the API
_ Saves you doing implementation you'll throw away

Continue writing to API as you flesh it out
_ Prevents nasty surprises
_ Code lives on as examples, unit tests

Writing to SPI is Even More Important

Service Provider Interface (SPI)

 Plug-in interface enabling multiple implementations 

Write multiple plug-ins before release
_ If you write one, it probably won't support another
_ If you write two, it will support more with difficulty
_ If you write three, it will work fine

Maintain Realistic Expectations

Most API designs are over-constrained
_ You won't be able to please everyone
_ Aim to displease everyone equally

Expect to make mistakes
_ A few years of real-world use will flush them out
_ Expect to evolve API


API Should Do One Thing and Do it Well
• Functionality should be easy to explain
_ If it's hard to name, that's generally a bad sign
_ Good names drive development
_ Be amenable to splitting and merging modules


API Should Be As Small As Possible But
No Smaller

When in doubt leave it out
_ Functionality, classes, methods, parameters, etc.
_ You can always add, but you can never remove

Look for a good power-to-weight ratio

This maximizes information hiding
• Allows modules to be used, understood, built,
tested, and debugged independently

Documentation Matters

Document every class, interface, method,
constructor, parameter, and exception

Class: what an instance represents
_ Method: contract between method and its client
_ Preconditions, postconditions, side-effects
_ Parameter: indicate units, form, ownership


 Document state space very carefully

• Take advantage of API-friendly features
_ Generics, varargs, enums, default arguments
• Know and avoid API traps and pitfalls
_ Finalizers, public static final arrays


Subclass Only Where It Makes Sense

• Subclassing implies substitutability (Liskov)
_ Subclass only when is-a relationship exists
_ Otherwise, use composition

Provide Programmatic Access to All
Data Available in String Form

Use most specific possible input parameter type
_ Moves error from runtime to compile time

• Don't use string if a better type exists
_ Strings are cumbersome, error-prone, and slow
• Don't use floating point for monetary values
_ Binary floating point causes inexact results!
• Use double (64 bits) rather than float (32 bits)
_ Precision loss is real, performance loss negligible

Avoid Long Parameter Lists

Three or fewer parameters is ideal
_ More and users will have to refer to docs

Long lists of identically typed params harmful
_ Programmers transpose parameters by mistake
_ Programs still compile, run, but misbehave!

Two techniques for shortening parameter lists
_ Break up method
_ Create helper class to hold parameters

Avoid Return Values that Demand
Exceptional Processing

return zero-length array or empty collection, not null



No comments:

Post a Comment