How not to design an SDK

March, 2022 · 5 min read

There’s enthusiasm in the air as we start exploring a new library. It’s maintained, and has a fair number of users. But frustration is about to set in.

FeelsBadMan
FeelsBadMan

The documentation takes forever to get to the relevant parts. The code examples? Talk about a worse signal-to-noise ratio. And once you scrutinize the code, things aren’t looking all too great.

Enter the JavaScript SDK for Microsoft Azure’s Blob Storage. Particularly v10, which was the latest version until 2020. Its documentation doesn’t seem intended for developers, and its API is a textbook example of poor object-oriented programming.

Azure Blob Storage

Blob Storage, like Amazon S3, is a service used for serving static websites and storing media, backups, and raw data in general. Objects you upload are called Blobs, and a Blob belongs to a Container.

Microsoft provides SDKs in a handful of languages, wrapping the underlying REST API. Every SDK comes with a Quickstart, serving as documentation.

Is it about JavaScript or the actual SDK?

Browsing the JavaScript SDK’s Quickstart, you need to scroll 50% through it before any real action happens.

Advanced concepts.
Advanced concepts.

It starts by telling you how to clone a repo and manage environment variables. Then, the advanced concepts of npm install and npm start both have their own Heading 2. What follows are explanations of fs and path, how variable assignment works, and why a top-level async function is needed. An additional page or two later, we finally begin seeing snippets demonstrating how to create a Container and upload a Blob.

I don’t want to sound ungrateful — I’d rather have this than nothing. But it wouldn’t be outlandish to assume that people seeking an SDK for a specific language have basic proficiency in it. It also strikes me as excessive to provide a line-by-line commentary of example code, intended to be simplistic in the first place.

Show us concise code, and do that right out the bat.

v12’s Quickstart contains even more information that feels excessive.
v12’s Quickstart contains even more information that feels excessive.

"Object-oriented" programming

Here’s a task for you: design an SDK for the Blob Storage. What is your ideal API for it? Remember, you want to be able to create Containers. You then want to be able to upload Blobs to them. Contemplate for a minute.

In v10 of the JavaScript SDK, this is the minimal code for uploading a Blob to a Container:

const {
  SharedKeyCredential,
  StorageURL,
  ServiceURL,
  ContainerURL,
  BlockBlobURL,
  Aborter,
} = require('@azure/storage-blob');

const STORAGE_ACCOUNT_NAME = 'johanli';
const ACCOUNT_ACCESS_KEY = 'something';

const container = 'myWebsite';
const blobName = 'index.html';
const body = 'Hello, World!';

const credentials = new SharedKeyCredential(
  STORAGE_ACCOUNT_NAME,
  ACCOUNT_ACCESS_KEY,
);
const pipeline = StorageURL.newPipeline(credentials);

const serviceURL = new ServiceURL(
  `https://${STORAGE_ACCOUNT_NAME}.blob.core.windows.net`,
  pipeline,
);
const containerURL = ContainerURL.fromServiceURL(serviceURL, container);
const blockBlobURL = BlockBlobURL.fromContainerURL(containerURL, blobName);

/*
 To omit serviceURL and containerURL:
 
 const blockBlobURL = new BlockBlobURL(
  `https://${STORAGE_ACCOUNT_NAME}.blob.core.windows.net/${container}`,
  pipeline,
);
 */

const aborter = Aborter.timeout(300000);

blockBlobURL.upload(aborter, body, body.length).then((response) => {
  console.log(response);
});
Minimal code for uploading a Blob to a Container.

Where do we even begin? First off, the SDK is coded in TypeScript and is heavily object-oriented. Nothing inherently wrong with that.

But read out loud the classes provided to us: SharedKeyCredential, StorageURL, ServiceURL, ContainerURL, and BlockBlobURL. A famous comic springs to mind:

"Object-oriented" programming.
"Object-oriented" programming.

It’s not quite as bad. But the numerous URL classes give you the impression of forming URLs for you. But, think again — their constructors require you to provide the entire URLs yourself. No kidding. Additionally, STORAGE_ACCOUNT_NAME has to be supplied both to SharedKeyCredential and ServiceURL.

Next up we’ve got pipeline, obtained from StorageURL. It’s an implementation detail. There’s no reason an SDK user should care about it unless it needs to be manually unclogged.

Finally, look at the upload method. An Aborter object, of all things, is the first argument. Why is it so important? Preferably, I’d want a default that I can override if necessary. Lastly, content length being a required argument also feels inconvenient when, in most cases, it’s something the SDK can figure out.

I’m being harsh. But keep in mind: this is a public SDK wrapping a REST API of a widely used service. It’s in its tenth major version. How is the SDK still this unpolished after being allowed so many incompatible API changes?

A good SDK

Let’s check out a good SDK for comparison’s sake. Chances are, it’s similar to your design. Here’s the minimal code, using Amazon Web Services’ SDK, to upload something:

const AWS = require('aws-sdk');

const container = 'myWebsite';
const blobName = 'index.html';
const body = 'Hello, World!';

const s3 = new AWS.S3({
  credentials: {
    accessKeyId: 'johanli',
    secretAccessKey: 'something',
  },
});

s3.upload({
  Bucket: container,
  Key: blobName,
  Body: body,
})
  .promise()
  .then((response) => {
    console.log(response);
  });
Demonstrating a better API for uploading a "Blob" to a "Container".

Beautiful. There’s no pipeline, pigeonPost, or smokeSignal going on here. You don’t stitch together URLs, and everything is sensibly named. Object-oriented programming done right.

Opinions about the Java SDK

Now, I’m not sure this counts as a data point — a certain profession claims it’s all the same — but someone criticized the Java version of the Blob Storage SDK. Other developers chime in, sharing the frustration. Here are some highlights from that GitHub issue, titled This API is useless (v10):

The API is an unintuitive mess. Documentation is rubbish and out of sync with code, and the example is useless.

I’ve been doing Java professional, every day, for 18 years, but never seen such a poor API.

stop developing on the version 10 API and Kill it. KILL IT WITH FIRE!

We’re not known for beating around the bush, are we? This is quite an indictment.

The naming of BlobSASSignatureValues is really bad. It tells me absolutely nothing about what its purpose is. I would think it is simply a POJO class for holding some values. The name indicate it is a class of values, not a class of functionality.

Remember all those classes ending in URL?

Another user posts:

We’ve also been using this (a team of advanced java developers) and have been struggling with getting it to work correctly.

Folks, it’s not a good sign when advanced developers have trouble using your REST API wrapper.

A third developer adds:

I am giving serious consideration to just calling the REST endpoints directly instead of using the SDK

Finally, the original poster drops the mic explaining what he expects from an SDK:

First of all it should help me save time getting my job done. If using the SDK will cost me more time that going low level and e.g. call a rest interface myself, the SDK has failed.

The SDK should hide the complexity about the service I’m using. I don’t care on bit if this SDK is communicating with the Azure service using REST, gRPC or rfc1149 as long as it is efficient. Using the SDK should make my codebase simpler than if I have not used it and it should improve maintainability.

The SDK should be self explanatory and intuitive. If I need to read more than a few lines getting started, performing simple tasks, it has failed.

Preach brother, preach! Give this man a gold star! And he’s even thrown in a subtle pigeon reference as well!

To be clear, I don’t think the JavaScript SDK is quite as bad — in fact, it’s acceptable. But for a run-of-the-mill SDK of a proprietary service, I’d expect better.

In closing

When creating an SDK, start with the user experience. Why are people using it and the service, and who are they? What would a good API be? Contemplate, look at similar SDKs for inspiration, and only then should you start coding yours.

We sometimes miss the forest for the trees. The SDK’s source code utilizes many OOP concepts such as interfaces, inheritance, abstract classes, factories, and private methods. Yet, it failed with the very essence of OOP in terms of its API.

References

Git repository (v10) and links to the Quickstart (v10 and v12):