Building a great SDK — Part 2

Diana De Rose
6 min readSep 28, 2019
men solving rubicks cube

Earlier today, I published a piece on Building a great SDK and wanted to follow up with few more tips and guidelines towards building an awesome SDK.

Core Guidelines for an SDK

In my last post, I listed the core components that an SDK should contain. The following section captures guidelines and best practices for all essential modules in an SDK. While it can seem like a big list of TODO’s, use this as a reference to build a checklist of your own.

boy holding a checklist

Open-source: SDKs should be open sourced and added to a public facing github repo. This increases transparency as well as encourages contribution from the developer community.

Naming conventions and Coding standards:

  1. Make sure to name the github repo such that it is easy for developers to find.
  2. Include language in the repo name if you plan to create SDKs in multiple languages. Example: QuickBooks-Java-SDK
  3. Refrain from ambiguous names and also consider extensibility. Example: Instead of naming a repo as v3-java-sdk, consider naming it as QuickBooks-Java-SDK that will allow you to extend it to future iterations of the API, as well as future offerings of QuickBooks.
  4. One repo can have multiple projects inside. For instance, QuickBooks SDK could have multiple sub projects or libraries for accounting, payment, payroll etc.

Follow language specific coding standards and guidelines. However, for key components make sure the ClassNames and core features are consistent across an SDK in multiple languages. While naming classes, make sure they are meaningful and avoid exposing internal abbreviations.

Packages: Make sure all classes are organized under packages with meaningful names. For example, service classes can be added under service package, auth related classes can be under oauth package, model classes under data package and so on.

Class Methods and parameters: While most Swagger generated SDKs provide an API class for each endpoint, this doesn’t add much value to developers as they need to keep track of each API endpoint to class mapping. If the API supports basic CRUD operations across all entities then create one single Service class and provide generic methods for each of the operations. Example-

  • Service.add
  • Service.update
  • Service.delete
  • Service.query

The service class can be prefixed with meaningful information such as PaymentService, AccountingService etc. There can be an exception to the above rule if the API has different operations for each entity; in which case, separate Service classes can be created for each entity. However, make sure to put any common logic into a Base class.

Comments: Comments increase readability of your code and eases maintenance. Add class level as well as method levels comments to document the class and method in a clear and concise manner. In addition, comments should be added wherever necessary to explain any complex flow.

Language Version: Before creating an SDK, gather data on the most widely used versions of the language for which the SDK is being created. An SDK should cater to the widest audience, as well as be extensible to future developers in the platform.

External dependencies: Avoid adding too many external libraries to an SDK. This causes tight coupling between the library and the SDK and can create issues when the library is deprecated, not maintained or has vulnerabilities. In cases where using an external library is a must, make sure to use a library that is widely used and doesn’t contain any security vulnerability. Use tools like Renovate or OWASP dependency check to scan for vulnerabilities.

Default values: Make sure that an SDK always has default values set for all parameters that are essential for making an API call so that the developer is not forced to supply values for each and every config parameter.

Testing: Test cases are an important part of the SDK and should be added before shipping the SDK.

  • Unit Test — Add unit tests for all important components of the SDK like service classes, utility classes, interceptors etc. Unit tests should be configured to run during the build process.
  • Regression — Regression test should be written for all the APIs that the SDK supports and should be set up using a Continuous Integration tool like Jenkins to run on a daily basis.

Documentation: Documentation of the SDK should contain links to the artifacts, start up guides, limitations, definitions, and any other offerings that would help building functions that leverage the API. Document the getting started guide such that a new developer is able to start using it in 5–10 minutes. In addition to the readme, make sure to include the following:

  1. Class library document
  2. Sample code
  3. Links to other guides like wiki, github pages or developer portal guides

Release process: Before releasing a new version of the SDK, make sure to run regression tests with the new changes and update the version number of the SDK.

  1. Publish to the package manager like nuget, maven, composer, npm etc.
  2. Document release notes in GitHub.
  3. Notify developers of changes either via GitHub or other channels.

Versioning the SDK: Once the initial version of an SDK is released, subsequent updates to it should be made by updating the major or minor version of the SDK. As a best practice, major version should be incremented when key features or components are added to the SDK while minor version should be incremented for bug fixes and minor enhancements.

Auto Generation of the SDK: — While SDKs can be auto generated using swagger and other public tools, often such SDKs don’t add much value to developers. Refrain from producing such SDKs unless it is a business need. Even in such instances, make sure to customize the SDK to provide additional value rather than shipping the SDK as-is. Core components of the SDK can however be automated; for instance, you can automate such that changes to the schema automatically generate new classes for your model component.

Statelessness: An SDK should be a stateless component and should not work as a mechanism to store and maintain client data. If functions are added in an SDK that change the state of data, care should be taken to ensure the client is made aware.

Example: an SDK should not auto refresh tokens without notifying the developer (client code); neither should an SDK be used for storing and managing the latest copy of tokens. Tokens are sensitive data and should be stored securely in the developer’s (client) database.

Extensibility: an SDK should be designed such that it is easily extensible to include new features, components, as well as support new version of the language that it is built in. Any time a new feature is added to the SDK, make sure it is backward compatible.

Note: While adding features that cater to one-off requests, build them as extensions rather than core features.

Validation: An SDK should not handle API validation and should pass the API request to the service layer as-is. This will enable the API to modify validations without impacting the SDK. An SDK should, however, validate that key variables (such as tokens, environment name, and client id) that are required to make an API call are provided.

Maintaining the SDK: Once the SDK is released, make sure to maintain the SDK and engage with the community.

  1. Support for newer versions of your language
  2. PR/Issues/Bug reporting — Monitor the issues being reported as well as PRs that are submitted.
  3. Engage with the community — Respond to questions and reach out to developers proactively requesting feedback before and after the SDK is released.
  4. Run regression and dependency check validation periodically.

Forward looking & some final thoughts

boy relaxing

Everything mentioned up until here are the bare minimum essentials in a good SDK. In addition to providing a wrapper around the APIs, an SDK should also consider adding an orchestration layer which can perform multiple operations under the hood. This can be done for common use cases that developers are likely to use. For example: In the accounting world, a common orchestration use case would be to create an invoice and billing time activity for the employee who performed the job or capturing a payment and creating a sales receipt for the payment.

Other great value adds can be adding a retry mechanism in the event of a failure, async operations, pagination support to query data in chunks and others. Although a lot of the examples above pertain to the Accounting world, these can be used in pretty much any context.

If you enjoyed this article, you may want to read the first article in the series Building a great SDK.

Have additional thoughts or ideas? Share them in the comments below.

--

--

Diana De Rose

Software Engineer with a passion for helping other engineers.