Shifting Perspectives: Placing API Design at the Core of Development

Mario Bittencourt
SSENSE-TECH
Published in
8 min readJun 30, 2023
Photo by Anastase Maragos on Unsplash

We have been living in an API world for over 20 years now. Our services likely consume and/or offer Application Programming Interfaces for internal and external audiences. This should mean that designing them is expected to be an important part of our development/product lifecycle.

Yet we still find ourselves debating whether we should focus on its definition as our primary task. This article aims to provide a pragmatic take on giving API the right importance and shares practical tips to succeed

API-First Approach

Unsurprisingly, API-first expects that whenever designing an application, you should begin with defining what the API will look like.

This contrasts with a more prevalent situation where we tend to start developing from the bottom up. First focusing on implementing the persistence model and all the components or layers that exist, and finally reaching the surface where we receive the requests from the clients.

There are many motivations to do so, let’s inspect the major ones and why they matter.

Your Window to the World

If your application will expose functionalities to others, in a private or public fashion, your APIs will be the entry point, i.e. the way that your clients will interact with you.

This entails a client-oriented mindset where the better the APIs, the easier their adoption.

By discussing this interaction with the client in mind you increase the chances of delivering something they will like to use, in line with their expectations.

Shorter Feedback Cycle

In the traditional approach, your clients only see and interact with the API after it is done. By then, any feedback and learnings will incur higher costs if you decide to make changes.

If we focus on defining the interface and deliver something they can interact with sooner, we will in turn be able to get feedback sooner.

Making changes becomes more affordable and the learnings from this interaction can be passed on to the other components of the application, potentially influencing your domain and persistence model.

Focus on Documentation and Testing

In the traditional approach, testing coverage is typically left to the end, so there is no usable API until then.

Consequently, the documentation is not created because we do not actually know what the interface will look like.

As the deadline looms, the focus is on delivering the APIs and these important parts may receive insufficient attention. The result is a technical debt that may take longer to repay.

Practical Tips

Let’s assume that you have a new service to offer and you decided to take the API route. You decided to use the REST style and opted for the API-first approach. What’s next?

Focus on Intent

Our initial step should be to focus on defining the intent, i.e. what our API is supposed to offer. Here, a tip is to avoid the pitfall of considering your API as a simple CRUD when it is not.

As simple as it seems, attempt to describe what your API will do in natural language. If you already have a ubiquitous language, stick to those terms when creating the description. Doing so results in a description that allows you to communicate with both the technical and non-technical members involved in the development process.

For example, you want to offer a service that will allow your clients to manage an appointment to visit your store and try on clothing. A description could be:

  • Allow customers to schedule appointments by providing the date and time of interest and a list of one or more items they would like to try
  • Allow customers to see their future appointments
  • Allow customers to cancel or reschedule appointments
  • Allow customers to add or remove items they want to try for a given appointment

With this definition in place, we are ready for the next step.

Create Your Documentation

You could say that the previous step is not exclusive to the API-first approach, and you would be right. The focus was on clarifying the intent and the domain language to be used.

In this step, we are going to create our live documentation. Here, there are two opposite takes:

  1. Create your documentation as part of the executable source code

Use some form of code annotation that is ignored by the actual execution but can be parsed to generate the documentation.

https://gist.github.com/mariobittencourt/1c82ac12b7f43ab40a9d2d4ee28dab22

In this example we are using JSDOC and adding OpenAPI annotations. Then, you can use swagger-jsdoc to output the actual specification that can later be rendered by API visualization tools like reDoc or SwaggerUI.

2. Create your documentation alongside the executable source code

You create the API documentation as a separate file — or set of files — that is hosted in the same source version control.

Figure 1. Separate files for the openAPI spec files and executable code.

You can use tools such as stoplight.io and have a preview of the API, alongside other benefits that we will discuss shortly.

Which Option to Choose

While this may be a controversial choice, I recommend the second one. By having a separate artifact dedicated to the API design, you do not clutter your source code with foreign annotations that can distract from the understanding. Additionally, you can leverage visual tools that remove the cognitive overhead of having to know the annotation elements.

The standard argument for using the first option is that it is closer to the code so has less chance of getting forgotten or out of sync. That, I feel, is not a valid reason because both options depend on non-executable code to be added. Essentially, they already represent an extra activity to be undertaken.

While the first option — depending on the programming language — can infer some types when generating the documentation, it falls short of providing details or examples that make the API truly rich.

In this way, the second option is cleaner and, if paired with the right tool, offers additional benefits.

Mocking From the API Design

So far our journey has led us to a better understanding of the functionality and a definition of the API that others can consult. That is already an achievement, but we can do more!

Wouldn’t it be nice if we could play with the API and see it in action prior to developing it? How about distributing it so our clients can interact with it as well?

Enter Prism, one of the many solutions that can take your OpenAPI specification and generate a mock server that can answer requests, both for success and failure cases.

We start by running Prism and pointing to the specification.

Figure 2. Starting Prism with a local specification.

You can then use your http client of choice to send a request.

Figure 3. Sending a request to the mock server. The response is a pre-created example in the openAPI specification.

Prism logs will indicate the received requests.

Figure 4. Prism logs showcasing the execution of requests.

If you are using a tool such as stoplight.io you can leverage that integration from the interface itself to start the mock server.

Figure 5. Stoplight Studio and direct prism integration.

With this in mind, you can make your specification available and your customers can try it in their environment.

Figure 6. As the team produces the spec, it is published for consumption by the clients that need to integrate it.

This reduces the need for each client to create their mocks for some of their tests and can unblock them with a contract that you provide.

Controlling Style

If your company has many services and APIs, wouldn’t it be nice if you could have a consistent experience while interacting with their specifications?

When we talk about consistent code, the usual solution would be to have a linter and integrate it with your build process, potentially preventing the code from being merged with clear violations.

We can have the same with APIs as well and an interesting tool to help us is Spectral, which allows you to define your style guide as a set of rules and parse the existing specification to flag deviations.

For example, you want to establish that all your APIs should have a health status endpoint to enable automatic or manual monitoring. We can create a rule, like below, in our style guide.

Then, when you run spectral you would get an output similar to this one.

Figure 7. Running spectral to check for violations.

You can define your ruleset and make it available internally so all teams and APIs adhere to it as you develop them.

When Should I Use it?

Given all that was presented, it is safe to say that it is a valuable strategy in most cases, with notable exceptions if you still haven’t decided what the expected functionality is.

In these cases, you are likely still prototyping not only with the implementation but with the definition of what the service will be. The interface will likely change too much from this concept phase until it can even be discussed with others.

Conclusion

Designing and developing APIs is part of our services and will continue to be so, even within a hybrid environment with asynchronous messaging involved.

Due to its importance, designing the interface should be as simple and inexpensive as possible.

If you start by clearing the functionality you will provide and then, with the help of tools, focus on exposing your vision, you will have a shorter feedback cycle. Complement that with auto-mocking capabilities and a uniform experience, and you have already addressed a good part of the common challenges.

To continue this trend, consider following a vertical development approach as well, and start providing the functionality your mocks expose in a gradual manner. This will enable you to deliver functionality that replaces the mocks quickly.

Editorial reviews by Catherine Heim.

Want to work with us? Click here to see all open positions at SSENSE!

--

--