In this series, I will share some guidelines on creating valuable, usable, and future-proof Enterprise APIs. In this initial post, I will cover some background context and a very important lesson, before getting more technical in parts two, three, and four.
If you’re wondering why this matters, I recommend reading about the API Economy.
In the last few years, thanks to my role as a Principal Solutions Architect at Palo Alto Networks, I’ve dealt with a lot of APIs. Most of these APIs were offered by Enterprise companies, predominately in Cybersecurity. By dealt with I mean that I’ve either written code to integrate with the APIs myself, or I’ve helped other developers do it. During some of these integrations, I had the privilege of discussing issues and potential improvements with the Engineers and Product Managers responsible for product APIs. Meeting with these stakeholders and providing feedback often lead to API improvements and a stronger partnership.
In this blog post, I will share some of the things I’ve learned about good API design that can lead to better business outcomes and fewer headaches for API builders and consumers, including better Developer Experience (DX) and more efficient operations.
There are tons of great resources out there about API best practices (such as the Google Cloud API Design Guide) and this post is not meant to be a comprehensive list, but rather present a set of common patterns that I ran into. Places where I usually find very interesting content are: APIs you won’t hate and Nordic APIs.
Before sharing the lessons I’ve learned, let me explain the context where most of my experience is based upon:
- B2B: Enterprise on-premise or SaaS products integrating with other Enterprise products (by partners) or custom applications/scripts (by customers).
- Top-down: some business mandate requires integrating with a specific product (i.e. because a joint customer was requesting an integration between two vendors), without the developers being able to choose an alternative that provided a better Developer Experience (DX). Sometimes we really had to bite the bullet and power through sub-optimal DX.
And in most cases we had the following goals and constraints:
- Speed: build something quickly and move on to the next project. That’s how many Enterprise companies work when it comes to technology partnerships. While this doesn’t sound ideal, there are several good business reasons: sales objection handling, customer acquisition, marketing, etc. You know that only a few ideas can be successful: similarly, many product integrations don’t move the needle, so it’s good to test many hypotheses quickly and invest properly only in the few that are worth it.
- Build with what we have: because of the above requirement, we rarely had the opportunity to request API or Product changes before we had to ship something.
- Create value: doing things quickly doesn’t mean you need to check a box that provides no value to customers. Even when the integration use cases were limited in scope, we always had an outcome in mind that could benefit some end-user.
- Performance: don’t create things that create performance issues on the APIs or, even worse, on the backing products. Or that could end up in significantly higher public cloud spending because they trigger expensive, non-optimized backend queries. APIs should protect themselves and the backing products from misuse but, in my experience, it’s not always the case, especially with on-premise products.
While I believe that a different context could lead to different conclusions, I think that the guidelines that follow are valid for most scenarios. Let’s begin with the most disruptive one.
0. Design first (if you can… but you really should)
Although it might sound like preaching to the choir, my first recommendation is to adopt the design-first approach when creating APIs. This also (especially!) holds true for Enterprise products even though it may sound hard because APIs are too often not perceived as first-class components of a product (or as products by themselves). I’ve seen a lot of different scenarios, including:
- APIs created to decouple the client logic from the server backend, without thinking about any other consumer than the product UI itself (a good example of a great idea made much less effective).
- APIs that basically just exposed the underlying database with some authentication in front of it.
- APIs created only to satisfy a very narrow/specific feature request coming from a customer and evolved in the same, very tactical, way, feature by feature.
- APIs meant to be internal-only that were later exposed to external consumers because of a business request. By the way, this breaks the so-called Bezos API Mandate and is usually a primary cause of technical debt.
The examples above often performed poorly when we tried to consume those APIs in real-world integration use cases, leading to poor DX and reliance on client-side logic to work around the problems, which many times translated into lower quality results and developer fatigue (more on this in part 4).
But such approaches aren’t necessarily wrong: arguably you can’t really know all the potential use cases of an API when you ship its first iteration and you gotta start somewhere but, if you understand that you don’t know what you don’t know, you can at least mitigate the risks. And the easiest and cheapest way to mitigate risks is to do it earlier on, hence I strongly recommend to embrace a design-first approach when creating APIs.
However, if you really want or are forced to follow a code-first approach, you can still apply most of the guidelines presented in this series as you develop your APIs.
In the next post, I’ll cover some security and backend considerations. In part three I’ll dive into some more technical considerations about data-driven APIs and scale. Finally, in the last chapter, I’ll present some more guidelines about monitoring and supporting your API and developer ecosystem.