How To Design a Good API

If you have not have already read the characteristics of a good API, you may do so in “Why To Design a Good API.”

As discussed in the above-mentioned article, API is almost permanent and once distributed, should not be modified. You have got one chance to make it right.

 

Principles to keep in mind during API design

  1. Functionality and readability of the API should be the top-most concern while developing an API. Object names should be self-explanatory and abbreviations should be avoided while naming an object.

    Try to attain a visual symmetry. This makes the API more readable and easy for users to associate with.

  2. Try to keep the API footprint as small as possible. However, the goal is not to strip the API of all its essentialities. It should meet all the requirements and not hamper functionality or readability.

  3. When there is a doubt with respect to implementation strategy or functionality of any aspect of the API, drop it. It is possible to add items to an API but it is a bad practice to remove items from API once it has been distributed.

  4. Target code/feature quality over quantity. This will always prove to be good in the long run, primarily because features can be added through multiple development cycles.

  5. Try to be well defined with respect to API features and implementation routines. This makes sure that developers have the highest flexibility. Implementation routines may also be included within the API, but it should be well segregated.

  6. Keep information hiding in mind while developing classes. Do not needlessly declare public objects unless it is specified within the specification sheet. This reduces possibility of bugs and also allows independent testing and debugging.

  7. No API can be a “good” API unless it has proper documentation. In fact, documentation is part of the API design. The developer should document everything, even internal codes should be well documented.

    Always try to use the platform native standards for documentation.

  8. APIs must never be bloated. Performance should be the primary target as it forms the inner layer of other products built around it. The focus is not to draw attention to the API, but rather to seamlessly blend with the platform.

    As a general inference, a good design has reasonably good performance.

  9. Your API should co-exist with the platform and with other products that can potentially co-habit with your product. Try to take advantage of native runtimes, and native data-types in order to boost performance, readability and platform integration.

  10. Use platform-native generics, enumerations, default arguments in order to make end-developer’s life a little less complex. This also helps reduce bugs and unnecessary support services.

  11. Target popular development IDEs for that particular platform. Use any additional formatting or directives that would use features of the IDE like code-completion, code-snippet, source-versioning, etc.

  12. Avoid codes that reduce code flexibility unless the API specification explicitly requires them. Finalizers and ReadOnly properties are examples of such codes that reduce flexibility.

  13. Finally: these are not everything and these are not comprehensive. Use your imagination!

 

Guidelines For Individual Stages of API Development

Data Collection

  1. Make a “realistic” expectation for the particular development cycle of the API. If it is the first cycle, try to adhere to all the characteristics of a good API.

    As is said: You cannot please everyone; so displease everyone equally.

  2. Gather initial requirements with a very flexible bent of mind. This will allow you to gather as much requirement as possible and then slowly condense to a more feasible specification sheet.

  3. Make use cases.

  4. Aim generalization and better abstraction of the specifications than detailed granularity. Abstractions can be digested later, but it is tough to form abstractions from implementations that are already too granular in nature.

  5. Make the specification sheet in two or more stages – the first one being preliminary specification sheet. Take inputs from developers, users, testers and usability experts. Concise specifications are easier to morph and mod.

  6. Initiate coding as soon as you complete the second level of specification sheet. Make abstract classes, object hierarchy and basic method design even before the full specification is ready.

  7. Write intermediate test codes and get them reviewed by usability analysts.

 

Designing Classes, Class Hierarchy and Interfaces

  1. Create class hierarchy as per real world model and only when it makes sense. Do not create sub-classes just for the sake of grouping or categorization; rather create sub-classes with methods that has nearly similar behavior.

  2. Minimize mutability ((Refer to http://www.javaranch.com/journal/2003/04/immutable.htm to know more about mutability.)) of objects in a class. This makes them thread-safe, simple and reusable.

  3. Have a small and well defined state space irrespective of its spatial or temporal complexity.

  4. Avoid usage of mutable methods like getLocation() and currentPosition().
    Replace them with properties with appropriate getter and setter methods. Internally, make sure that users can directly work with the property results without bothering about performance penalties.

    When getting property values, try to return a shallow copy of an internal variable without performing any calculations. This allows users to directly use property values repeatedly without instantiating a local variable.

Designing Methods, Properties and Access Variables.

  1. Avoid designs where users have to repeatedly use same intermediate codes to achieve results. Wrap up all such codes within API. This would help reduce unneeded bugs and would also make the end-developer code more readable.

    // bad example
    for(int i = 0; i < 360; i++) myAPI.drawEllipsePoint(i);
    
    // good example
    myAPI.drawEllipseArc(int startingPoint, int endingPoint);
  2. Name a method keeping the verb in mind and not the output of the function. It has been observed that many API developers name functions from the perspective of the API itself. However, the good practice is to name it from the end-developers’ perspective.

    // bad example of method name to disable a timer
    boolean myAPITimer.stopped();
    
    // good example
    boolean myAPITimer.stopTicking();
  3. Overloading of methods should be done with certain stringent points in mind. Avoid having two overloaded methods with same number of parameters. And in case that is not avoidable, make sure that the overloaded functions have similar behavior.
  4. Keep the method parameter/argument data-types to be as specific as possible. This helps user understand the nature of the method even without reading the documentation. It also converts run-time errors to compilation errors that are easier to trap and debug.

    // bad example method and parameter name
    string myAPI.gErr(string state);
    
    // good example
    ErrorStateResult myAPI.getErrorState(ErrorState currentErrorState);
  5. Whenever a parameter requires fixed states as input or a small and finite set fixed values, try using enumerations and constants.
  6. Using a long list of parameters is less readable and end-developers will need to refer documentation more often. Three or less parameters is ideal. In case the implementation demands a long list of parameters, try breaking up the method into two or more methods. Using a helper class to generate a list of parameters is the most suitable solution in case the API design restricts partitioning of methods.
  7. End- developers should not require to do exception processing for any return value of your API.

    // example of unusable return type
    int strLen(string aString); // returns null when aString is empty

    In the above code, the end-developer needs to every time check for null and appropriately handle it. Whereas, a good API will always return 0 even if the parameter ‘aString’ is empty.

  8. Do not use low precision variables unless the API specification demands it. The spatial overhead is negligible compared to the usability loss for end-developers.
  9. When there is an error, raise/throw it immediately. Do not wait for an execution end-point to raise a cumulative error.
  10. Do not suppress an exception and fail silently and when being loud while throwing exceptions, do ensure to be as verbose as possible. Include an error reference number and a error capture message.

 

I have followed the footsteps of many experienced developers while formulating my set of points. All credit goes to whosoever was involved in educating me.

2 Replies to “How To Design a Good API”

  1. Hi,
    Nice article, Can you please tell me the elements -I mean document structure- to be included in an API documentation?

    Regards,
    Jobin

  2. Hi,
    Nice article, Can you please tell me the elements -I mean document structure- to be included in an API documentation?

    Regards,
    Jobin

Leave a Reply