Code

API design: The developer's UX

According to a study by Google, a user needs around 50 milliseconds on the web to decide whether they like the website they are currently visiting or not [1].

In a fraction of a second, the user decides whether to stay on the page or leave it again immediately. In order to Usability and thus also the user experience of software applications, a lot of time and money is invested in user behavior analysis, design and testing. This is also commonly referred to as User Centered Design (UCD). You want to offer users the best experience in order to keep them loyal to your service.

The developer experience - and the frustration of poor API design

Developers don't have to decide within 50 milliseconds whether they think an API is good or bad. But you can still tell quite quickly whether they are satisfied with the quality of an API. However, they often have no choice but to use the API. There can be many reasons for this: Sometimes the customer has a contractual partner whose system needs to be integrated, sometimes it's another microservice that needs to be integrated into the overall system. But if developers have to use existing APIs anyway, why should they bother with something like a Developer Experience (DX)[2]?

The answer is as simple as it is obvious: money. It costs a lot of money (and nerves) to use poorly designed APIs. I think every developer has had the pleasure of connecting a bad API in their career. Characteristics of an inadequate API can be

  • It is poorly documented and misleading
  • the end points are designed inconclusively
  • the HTTP methods are inconsistent and contrary to the definition or return values
  • Error codes are incomplete[3].

It takes a lot of time to untangle these "knots". Misleading documentation can lead to bugs that take a long time to analyze and fix. Incomplete error codes and return values can lead to cumbersome workarounds. The supposed security of a GET request can manipulate data. The list of causes for errors in the connection of an API is endless.

Apart from the costs, a poor API does not reflect well on the underlying service. Let's think of the comparison with the UI again. A service with a poor UI and an inconclusive UX is reluctant to be used.

Therefore, the goal should always be to design a good API right from the start. Like so many things in software development, API design is also a matter of taste to a certain extent. It is therefore very important to define and agree on an API guideline within the company or project. At doubleSlash, we attach great importance to quality and have defined our own API design guidelines. We apply our standards in every project that we manage in order to design a profitable API.

API documentation: Trust is good - control is better

Many projects have integrated Sonar into the development environment to monitor code quality. In the best case, a central SonarQube server is still running, which displays the code quality measurably using many metrics. The same is possible for API documentation. The open source community has provided some tools for static code analysis (linter) for this purpose. A distinction is made between two different approaches - the centralized and decentralized approach. Zalando has a centralized approach with Zally[4]. Here, a central server is used against which the entire company validates its API documentation. Responsibility therefore lies with the team that operates this server. All teams must comply with the guidelines of this server. Stoplight, on the other hand, has a decentralized approach with Spectral[5]. They offer a CLI tool that validates the API documentation using a configuration file.

Both approaches are very easy to integrate into a build pipeline. Both Zally and Spectral have a CLI tool that can be easily integrated into an existing pipeline as a build step. For Spectral, the configuration for GitLab looks like this:

stages:
 - test

spectralLint:
 stage: test
 image: node:17.1.0-slim
 script:
   - "npx @stoplight/spectral-cli lint path/to/api/*.yml"

For Zally you have to download the tool from the release page: https://github.com/zalando/zally/releases and place it in the project. The configuration then looks something like this:

stages:
 - test

zallyLint:
 stage: test
 script:
  - "path/to/zally --linter-service https://url.to.zallyserver --format pretty lint path/to/api/*.yaml"

Zally also has an unofficial Maven plugin that breaks the sense of centralized API linking. The configuration for this looks like this:

stages:
 - test

zallyLint:
 stage: test
 image: maven:3-jdk-11
 script: "mvn verify"

and the pom like this:

<plugin>
    <groupId>com.ethlo.zally</groupId>
    <artifactId>zally-maven-plugin</artifactId>
    <version>${zally.version}</version>
    
    <configuration>
        <failOn>MUST</failOn>
        <specFile>path/to/api</specFile>
        <resultFile>target/api_validation_result.yaml</resultFile>
    </configuration>
    
    <executions>
        <execution>
            <goals>
                <goal>report</goal>
                <goal>validate</goal>
            </goals>
        </execution>
    </executions>
    
    <dependencies>
        <dependency>
            <groupId>org.zalando</groupId>
            <artifactId>zally-ruleset-zalando</artifactId>
            <version>2.1.0</version>
        </dependency>
    </dependencies>
</plugin>	

Well-designed API - and now?

Now that the API has been uniformly designed and complies with the guidelines, you might think that the work is done. But what happens in the course of maintenance and further development? During development, it may well be that the API is changed by manipulating classes.
A variable could be accidentally renamed without realizing that this changes the API. Change requests can also come in that have been implemented in the code but not in the API documentation. As with any documentation, the likelihood that it no longer matches the code increases over time.

It therefore makes sense to also validate the code with the documentation.

Unfortunately, there are no acceptable Java libraries that can be used to write a good unit test for comparison. Therefore, you have to fall back on CLI tools. With the OpenAPI Diff CLI from Atlassian[7], a "nightly validation job" could be configured in this way. In order to be able to use the validation job productively, the service needs an endpoint that generates the openAPI specification from the code and releases it to the outside world. In this example, we will use springdoc[8] for help.

The dependency only needs to be added to the Maven dependencies:

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.2.32</version>
</dependency>
 

Now you can set up a build job that compares the generated openAPI documentation with the specification during execution:

compare:
  stage: test
  image: node:17.1.0-slim
  script: npx openapi-diff http://url.to.service/v3/api-docs localOpenApiDoc.yml

Qualitative API design: The doubleSlash REST API guidelines

We at doubleSlash have opted for Spectral as our API linter. To this end, we have mapped our API guidelines in a rule set. We have decided that we want to make this rule set public so that other teams and companies can also benefit from it. We have published this rule set as an NPM package in the NPM registry. We have also published the rule sets on GitHub provided. In order to access this rule set, the rule set must first be imported via a package.json file:

package.json

{
   "dependencies": {
   "@doubleslashde/rest-complete-set": "1.1.0",
   "@stoplight/spectral-cli": "^6.1.1"
   },
   "scripts": {
   "lint": "spectral lint path/to/api/*.yml"
   }
}

It is also necessary to use the rule set in the .spectral file:

.spectral

extends:    
   - '@doubleslashde/rest-complete-set'

Once this is done, the CI/CD pipeline only needs to install the dependencies and execute the script:

npm install<br>npm run lint

Conclusion: Good API design for a good developer experience

As we have now seen, it is very important to design a good API in order to create a good developer experience. To ensure this, however, it is not enough to "just" create a guideline and rely on the fact that it can be implemented in exactly the same way. A static code analysis is essential here. For the same reason that you use SonarQube to ensure the quality of the code, you should also use a linter for the APIs to ensure the quality of the endpoints. Spectral is a very good CLI tool that you can use to check your documentation against the guidelines.

Sometimes, as a developer, you don't realize the damage you can do if you invest too little time in designing a good API. Perhaps there is no budget available at the moment or the focus is on other features. With a linter, you can fight against such dynamics and always ensure good API documentation. I hope you will take this article to heart when making your next API design decision and maybe even consider testing your APIs with a linter. Spectral already provides a detailed ruleset out of the box that you can use as a basis for your APIs.

Have fun trying it out. Please let us know if your API design has been improved by a linter.

 

Learn more about user experience

Sources:

[1] https://research.google/pubs/pub38315/
[2] https://hackernoon.com/the-best-practices-for-a-great-developer-experience-dx-9036834382b0
[3] https://desmart.com/blog/why-do-we-create-bad-apis
[4] https://github.com/zalando/zally
[5] https://github.com/stoplightio/spectral
[6] https://blog.logrocket.com/common-api-mistakes-and-how-to-avoid-them-804fbcb9cc4b/
[7] https://bitbucket.org/atlassian/openapi-diff/src/master/
[8] https://springdoc.org/

Sebastian Lohr

About ME

All contributions from Sebastian Lohr

Learn more

Further information on our website and in our newsletter

Arrow up