Clean API Best Practices for Sustainable Microservice Development

Huseyin Kutluca
Software Architecture Foundations
4 min readOct 28, 2022

Clean and sustainable microservice development required software to be decomposed into modules that has high cohesion and low coupling. One of the key design solution to achieve such goal is to design these components as modules that hides all design details and provides service to other modules through its API.

These modules can be domain algorithms, protocol or standard implementation or infrastructure component that other modules depends on. Good structured microservice includes multiple API based components together with one service module that glues these components and other reusable components from other repositories. This service module includes these components initializations together with controller logic that transitions data among modules.

In this article we will discuss common mistakes being made and best practices to avoid these mistakes while implementing such APIs. We will not include protocol based APIs like Rest, GRPC or data centric(event centric) APIs into our discussion.

Common Mistakes

While implementing your microservice you design your code as modules and it is good practice that these modules are also interact using their APIs. Some of the common mistakes made while designing such APIs are;

  • Methods with too much parameters
  • Method parameters which are not consistent among other methods
  • API has implementation specific includes.
  • API has private members and methods
  • Multiple API methods for single feature(more than 2 or 3)
  • APIs with no error handling
  • Multiple classes in single header.
  • Limited in code documentation
  • No example codes
  • No Test codes

These common mistakes make use of such APIs very difficult results in unsustainable software. Now lets go through best practices that would help us to design better API.

Clean API

While designing API we should model the problem domain and create easy to use APIs. Our API should be optimally complete, not having too much capability in one API.

As we pointed out in common mistakes our API should hide implementation details, member variables, implementation methods and implementation classes. Our interface method names shall be clear and intuitive. These methods shall not include more than 3 parameters. API return values shall not be handled using parameters.

The proposed solution for clean API is to separate interface from library implementations. Abstract interface with factory pattern or Pointer to Impl (PIMP) idiom are commonly used solutions. Private methods members, implementation specific headers can be included in the implementation classes. These implementation classes are hidden from the API user. API users are also don't need to include headers that your implementation depends on. Thus preventing design leaks.

We know that we should apply clean code principals to all over our code. We should be much more tedious on clean code principals on our APIs as these are the main interfaces for our system integrations.

You should put your API under namespace/module context. That way your API specific classes, enumerations and definitions does not get conflict with other libraries.

When we implement these components with API, we should make sure that these components can be reused by other microservices without requiring big inheritance dependencies or other multiple large modules where only limited functionality is utilized by reusable component.

Outbound Interfaces

One of the main issues with API is how to return event or data to user application. Application software can implement abstract interface and give its reference to API at initialization time. Other solution is to register a callback function.

Callback functions from library is generally done from a library local thread that already handles critical tasks. So for user application it is strongly recommended to put received data into queue or somewhere and return from callback. Doing long work in callback function may degrade the performance of library drastically. If you use blocking operations or wait for mutex inside this callback, you may even cause deadlocks in the system.

Documentation

As API of your library will potentially be used by multiple microservices, interface documentation becomes critical. I would suggest to use Doxygen style for API methods. That way documentation stays inline with code.

We should also provide getting started document as markdown document with our code repository. This document shall clearly explain, installation, build, configuration steps. It can also include how to use related documentation. You can add C4 Container diagram showing your components relation with other possible software components. It will be really helpful if you include one sequence diagram showing initialization and use of API by other applications.

Tests and Examples

Test and example codes are also important part of API. These code pieces will help users of your library to use your API efficiently. Try to provide them with your proposed best practices for using API.

The test codes will also be used as verification of your API.

You should also provide mock code of your API for your selected test framework(Google GTest for example). This will help application developer to unit test their own code without need to mock your code each time.

Management

Common components that are being used by more than one microservices shall also be managed in separate code repository. If the components is big enough then you may create totally separate repository for this reusable component. Sometimes it is more convenient to add multiple common components into single repository. If you prefer this approach make sure that they are closely related and their documentation, and folder structure has been clearly separated in same repository.

--

--

Huseyin Kutluca
Software Architecture Foundations

Highly motivated Software Architect with hands-on experience in design and development of mission critical distributed systems.