✨ The 7 Golden Rules for Creating API Libraries that Shine ✨

Beppe CataneseBeppe Catanese
8 min read

API libraries are essential in making APIs accessible to every developer. They create an abstraction that simplifies how applications interact with services. They boost productivity by fitting in the developer’s preferred language and technology stack. They let developers focus on creating business value instead of worrying about repetitive work and boilerplate code.

However, creating a successful API library is more than just wrapping API calls. It’s about understanding the audience’s needs and pain points to deliver an optimal developer experience.

In this article… discover the 7 Golden Rules for designing API Libraries that make Developer Experience a top priority. Based on a true story (Adyen 💚).

Read below the benefits, challenges and tips of the 7 golden rules:

  1. OpenAPI Driven

  2. Idiomatic

  3. Release Notes and Breaking Changes guide

  4. Code snippets

  5. Reference implementation

  6. Deprecation strategy

  7. Great documentation

1. OpenAPI Driven

Nowadays, there are other options than writing the source code of the libraries from scratch. First, in any modern platform, API release cycles are short, and teams frequently introduce new features and changes. Second, accuracy is crucial: as we add functions, objects, and fields, we must ensure they consistently represent the API behaviour.

📣 Enter OpenAPI.

The answer is automated code generation. The source code can be generated by leveraging an OpenAPI specification and choosing the right tool (OpenAPI Generator, APIMatic, etc.).

These generators support multiple languages, are customisable, and can be integrated into a CI/CD pipeline.

How does it then work? Once the OpenAPI contract is finalised (whether you’re design-first or code-first), a pipeline triggers the code generation, followed by testing and releasing.

What happens next is the exciting part: as we need to evolve the API or address an issue, we go back to the drawing board and update the OpenAPI specification. There are no manual changes (except for some utilities and helpers outside the code generation), but the update follows the OpenAPI-driven lifecycle.

Lifecycle of the OpenAPI-driven library

Lifecycle of the OpenAPI-driven library — Image by author

The benefits are clear:

  • Automation: OpenAPI specs are converted to working code in an automated fashion, cutting significantly the release cycle (and effort) of the libraries

  • Consistency: The generated source code reflects the API contracts, removing discrepancies between consumers and producers.

Tips

Invest in the source code templates

The generated source code is based on code templates provided by the generators. However, you want to control the output (package structure, naming conventions, signature of the methods, comments, documentation, etc.), so the first essential step is to override the default templates.

Generator upgrades

Your generator of choice will keep evolving, and it’s essential to ensure that your custom templates remain compatible. Stay proactive, follow the upgrades, adjust your templates, and define a good testing strategy that verifies those upgrades don’t affect your library.

Code duplication

Code duplication is often seen as a critical metric in assessing software health. However, a different perspective is necessary when it comes to auto-generated code. The code templates process a list of endpoints and models, creating consistent and repeatable code blocks. As a result, code duplication becomes a “necessary evil.” The primary goal is to provide consistent, readable code; duplication should not compromise that.

Adjust your metrics accordingly to align with this approach. Tune SonarQube and other static code analysis tools to disregard duplication and focus instead on other metrics.

Drop and (re)generate

Before generation, remove all previously generated code, including services (endpoints) and models. This ensures that the library will contain only the operations and entities found in the OpenAPI specifications. Any elements deleted from the API contract must also be removed from the library.

Living downstream

SDKs are downstream products of APIs, which increases the complexity of development. API teams may add new models instead of reusing existing ones or overlook naming conventions (tip: enable a good linting mechanism), impacting the quality of the generated source code. Ensure you’re heard: state your requirements, emphasise the potential impact on the libraries and their users, and establish a feedback loop.

2. Idiomatic Code

Libraries are powerful because they fit in the technical context that developers are familiar with. Therefore, we need to ensure the generated code is crafted for the programming language it’s targeting.

An API library should align with the language’s conventional practices:

  • Naming Conventions: adopt the expected style guides (e.g. camelCase in JavaScript, snake_case in Python).

  • Error Handling: implement the property mechanisms for error handling (e.g. exceptions in Python, error values in Go).

  • Data Types: use native data structures to keep code readable and intuitive.

  • Language Features: Rely on the features of the language (e.g. different languages have different approaches to enums), making it easier for developers to understand and adopt the library.

Tips

Multiple languages

For teams that support multiple programming languages, it can be tempting to streamline the code generation by consolidating common aspects — for example, handling enums or comments in the same way across languages. However, this approach risks producing “foreign” code that doesn’t feel natural for the developers.

Start by always defining the desired output for each language and design your templates accordingly. Ensure the team is building strong expertise in each supported language. Rather than automating for simplicity or speed, focus on quality: keep revising the generated source code, adapt it to each language, and strive to deliver an experience that is aligned with the language’s conventions and idioms.

Dependencies and frameworks

Ideally, your library should be simple and lightweight. In practice, however, most languages are popular because of the rich ecosystem of frameworks and utilities. Try to minimise dependency imports, but make intentional choices when needed: frameworks can give developers a significant advantage.

Sometimes, it’s best to give developers flexibility — for example, by letting them choose their own JSON serialising library. In other scenarios, leveraging a popular framework can improve usability and simplify the integration.

3. Release Notes and Breaking Changes

The OpenAPI-driven approach increases the speed of delivery, which brings the challenge of providing good release notes for each new release.

New features must be documented, and changes should be explained. When introducing breaking changes, a robust API library handles the upgrade, clarifying the migration path and trying to minimise disruption in the existing applications.

Tips

Producing practical release notes is not trivial. It requires a careful analysis of the OpenAPI changes and a thorough review of extensive Git diffs to understand and summarise the changes introduced in each release.

Consider adopting a tool like OpenAPI, such as OpenAPI Diff or OAS Diff. This tool can produce a detailed comparison between two versions of the same OpenAPI specification. The overview helps pinpoint changes that must be highlighted or explained in the release notes.

4. Code Snippets

Good examples make a library accessible to all levels of developers; therefore, providing code snippets is essential to show how to use the library effectively.

Snippets are best placed within the product documentation. As developers learn the features and what to expect, the code snippets clearly indicate how simple it’s to use the library.

When you support SDKs in multiple languages, the snippets should be provided in different languages.

Code snippets in multiple languages

Code snippets in multiple languages — image by author

What makes a good code snippet:

  • it compiles and runs

  • it’s concise but remains clear and readable

  • it includes everything the developers need (imports, variable declarations, etc.)

  • it’s ready to go (a.k.a. copy-paste)

What about Unit testing

Unit testing (because who doesn’t love unit testing?) is also a powerful way to provide code snippets. As we increase test coverage, we give the developers more examples to learn from.

Unit tests invoke library features exactly as developers would, serving as a blueprint. In any open-source library, unit tests should become a public catalog of working snippets, demonstrating how to use the library effectively. That’s another good reason to invest in unit testing.

Tips

As the API evolves, so must the SDK and code snippets. Manually crafting these snippets (similar to Release Notes) can be tedious and costly, so we need an automation strategy. With an OpenAPI-driven approach, the OpenAPI specification is the single source of truth. By leveraging the examples in the OpenAPI file, we can generate code snippets that developers can use. When new API versions are released, the code generator updates the snippets and archives previous versions to maintain a comprehensive version history.

The Golden OpenAPI pipeline 

The Golden OpenAPI pipeline — image by author

5. Reference implementation

Nothing beats working code 🤓.

The most effective “Getting Started” guide is a sample application that demonstrates the library in action and covers the key use cases and workflows.

With the source code available on GitHub, developers can easily view what the library can do by cloning or forking the repository. They can explore the code, run the application, try out the features, and copy the code snippets.

Adyen Sample app on GitHub

Adyen Sample app on GitHub — image by author

Tips

Make the application available on Gitpod or Codespaces to allow developers to launch the application effortlessly.

Update the reference implementation when a new library release is published. This will showcase the library upgrade and provide developers with valuable information about the impact of the changes introduced in new releases.

6. Provide Deprecation Markers

Deprecation markers are necessary to guide developers when parts of an API become obsolete. The API library should follow language-specific conventions and mark the deprecated code accordingly.

Tips

OpenAPI supports deprecation for operations and fields, so during the code generation, ensure these tags are translated into language-specific annotations. If the OpenAPI file includes custom extensions, for example, a deprecation message (x-deprecatedMessage), configure the code generation to incorporate these messages and give developers extra context — directly inside the library’s source code and visible in the IDE.

7. Great Documentation

Great products need excellent documentation. Developers will inevitably land on the documentation page, whether they’re looking to get started or for guidance on optimisation or advanced features.

Documentation should serve as the gateway to the library, offering:

  • comprehensive guides to get started, integrate seamlessly, and make the most of the library

  • code snippets that show practical usage

  • links to source code on GitHub and the OpenAPI specifications

  • easy access to additional resources, including release notes, reference implementations, blogs, and videos

Conclusion

A successful API library goes beyond simply wrapping API endpoints; it prioritises developer experience. This means maintaining short release cycles while delivering high-quality code, offering code snippets and working samples, deprecation information, release notes, and excellent documentation.

Check out the ✨ 7 Golden Rules ✨ of API libraries, and let me know what else we can do to help developers create the best API integrations.

0
Subscribe to my newsletter

Read articles from Beppe Catanese directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Beppe Catanese
Beppe Catanese