Good API

  1. EACH DEVELOPER IS AN API DESIGNER (WHETHER IT IS A PUBLIC OR INTERNAL API)
  2. GOOD NAMES DRIVE GOOD DESIGN
    1. names should be easy to learn: BAD: AffAcct, GOOD: AffiliatedAccount; spend time on getting good names
    2. code should be easy to ready, good names make the code read like prose
    3. don’t copy/transliterate API (when re-writing code, e.g. from C++ to Java, don’t write complementary implementation for each method in each C++ class, but take a step back and think of class behavior and write it in Java)
    4. when you don’t know how to call a method perhaps it needs to be splitted or merged to get a good name
    5. names should be self explanatory as if no documentation was needed to understand what they mean
    6. obey platform naming convention and use consistent names (the same words throughout the API mean the same, don’t use different words for the same thing, use either delete or remove but not both of them, so that we don’t need to spend time to find out the difference)
    7. spend time to learn the project terminology
  3. API SHOULD BE EXACTLY WHAT IS SAYS
    1. a method should do one thing and do it well, e.g.: boolean interrupted() in Thread class should only check if the thread was interrupted, but not reset the interrupted status of current thread
    2. query command pattern: method should either query for sth or change the state, but not both at the same time
    3. API should be hard to misuse:
    4. avoid ambiguous method overloading (for arguments of the same hierarchy), preferably use different method names; if you need to overload – ensure the same behavior for the same arguments (e.g. by dynamic casting via instanceof), BAD: 3 methods overloaded that accept as a parameters that are of the same class hierarchy: e.g a collection, set and list – a call with parameter new HashSet() and (Collection) HashSet would dispatch to different methods even if the runtime is HashSet.
  4. WHEN IN DOUBT – LEAVE IT OUT
    1. API should be reasonably small
    2. you can always add a missing functionality later on but never remove it once clients already use it
    3. for internal use, it is still better to think twice about good design rather than do heavy refactoring later on
  5. USE DATA ABSTRACTION
    1. keep API behind a well defined interface and build future implementation based on that interface
    2. keep exceptions at the same level of abstraction (e.g. don’t throw SQL exception in business layer)
    3. implementation details should not impact API (e.g. don’t overspecify the behavior of the methods; mentioned SQL exceptions)
    4. information hiding (make classes, members as private as possible)
  6. MINIMIZE COUPLING
    1. easier to test, use and debug
    2. simplifies API
    3. can be achived by reducing accessibility, when in doubt make it private
    4. not coupled modules can be tested independently by different teams
  7. MINIMIZE MUTABILITY
    1. advantages: simple, thread-safe, reusable;
    2. disadvantage: separate object for each value
    3. if mutable – keep state space small
  8. FAVOUR COMPOSITION OVER INHERITANCE
    1. avoid subclassing classes from different packages that you don’t have control over
    2. inheritance violates encapsulation: if a functionality of a subclass depends on the internal implementation of the base class, then a change the in the base class may break the code in the subclass: e.g. CoutingHashSet overriding add(…) and addAll(…), and base class addAll(…) internally now uses add(…) but may not in future since it is its internals.
    3. subclass only if you are really sure that every instance of a subclass is a instance of a superclass (LSP)
  9. DRIVE YOUR API BY USE CASES
    1. use case can save from writing extra unnecessary API, you do only what you need to
    2. consult your API before you start writing an implementation, if the API is broken you will not waste your time for implementation that you will need to throw away eventually
    3. requirements should be translated into use cases in terms of general solution outline rather than specific implementation details
    4. don’t over-specify API to allow different implementations in future
    5. start with one page specs and consult that with stakeholders
    6. write examples to be published how to use your API and get it right because it will be reused a lot
    7. maintain these use cases as tests, examples; example programs should be very good quality because this is how people learn to use your API, don’t get them wrong
    8. don’t make the client do what module could do for the client directly, this reduces boilerplate code: e.g.: provide directly method writing a document into the OutputStream instead of forcing a client to instanciate a output stream transformer, converting document into appropriate format for transformer and eventually transforming the document plus catching exception that should never happen
  10. METHOD PARAMETERS
    1. try not to have identically typed parameters, so that you don’t mistake the order
    2. favour interface types over classes for input parameters to achieve flexibility
    3. use consistent parameter ordering across methods (follow one rule: first param: src, second: dst; stick to it and don’t mix it)
    4. limit number of method parameters to three (the fewer, the better) by breaking up methods or creating helper classes to hold parameters
  11. RETURN TYPES
    1. avoid returning values that demand exceptional processing, e.g. instead of null, return zero-length array or empty collection
  12. EXCEPTIONS
    1. validate at compile time, use most specific input parameter type (as oppose to the general type Object)
    2. validate arguments to report errors as soon as possible
  13. PRECISION
    1. use BigDecimal, long, BigInteger for monetary results instead of float or double
    2. use double (64 bit) rather than float (32 bit)
  14. OTHER
    1. write API documentation, e.g. preconditions, side-effects for methods; value units for method parameters (KB or Mbit etc), paremater forms (if it is XML format)
    2. bad API can decrease performance (e.g. exposing public contructors instead static factory methods returning cached instances)
    3. use API-friendly features, e.g. enums, varargs, generics, default arguments
    4. if a method returns lots of information, don’t return all of it in the String format (client doesn’t need to parse it), but provide accessor methods to the information details
    5. don’t use String if a better type exists: boolean instead String ‘yes’/’no’
    6. use BigDecimal, long, BigInteger for monetary results instead of float or double (lost of precision)
    7. mimic the core API and pattern used in the platform because people already know how to use it
    8. eliminating state makes API easier and less error prone
    9. expect to make mistakes and it will evolve.
    10. review often your API with other people
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s