# When an SDK Is Better Than Documentation

When I implemented [distributed memoization](https://vsdepontes.com/distributed-memoization-avoid-repeating-expensive-work), using the pattern correctly required more than just calling an API.

A consumer had to check the result store, generate the right key, deserialize the result, handle a cache miss, call the original service, and decide what to do if the cache was unavailable.

I could document all of that.

But then every consumer would have to implement it.

Instead, I created an SDK.

An SDK, or Software Development Kit, is a package that gives developers a simpler interface for interacting with a system or capability. It can wrap APIs, configuration, authentication, retries, data transformations, and other implementation details behind code that is easier to use consistently.

This becomes especially valuable in a microservices architecture, where several independent services may consume the same capability. Without a shared abstraction, small implementation differences can easily spread across the system.

In this case, the SDK wasn't only about code reuse. It turned the expected way of using the architecture into code.

## Hiding the integration

Without an SDK, a consumer might end up with something like this:

```typescript
const key = createResultKey("sales-report", 3, input);

const cachedResult = await resultStore.get(key);

if (cachedResult) {
  return JSON.parse(cachedResult);
}

return reportService.generate(input);
```

This doesn't look particularly complicated.

The problem appears when five services need to do it.

One might normalize the input differently. Another might forget the function version. One might fail the whole request when the result store is unavailable, while another falls back to the service.

In a microservices system, this is particularly easy to run into. Each service is developed and deployed independently, often by different teams and on different schedules. If every service implements the integration itself, those implementations can slowly diverge.

Even with good documentation, we're still asking every consumer to understand and reproduce the same decisions.

With an SDK, the consumer can see something closer to this:

```typescript
const report = await reportClient.generateSalesReport(input);
```

Internally, the SDK handles the result lookup and falls back to the service when necessary.

![Diagram showing a consumer sending a request through an SDK. The SDK first checks a result store for a cached response. If no cached result is found, a cache miss occurs and the SDK forwards the request to the service.](https://cdn.hashnode.com/uploads/covers/6a84cd5fe017e4d736be0124/4bef5032-6961-4d90-be1e-8faba595ae4e.png align="center")

The consumer doesn't need to know how keys are generated or how the fallback works.

It only needs to know how to generate a report.

## Code reuse is only part of the value

Shared functions could also remove some duplication.

An SDK goes a little further because it can define the boundary through which consumers interact with a capability.

For distributed memoization, that meant centralizing things such as:

*   input normalization and key generation;
    
*   serialization and deserialization;
    
*   cache lookup behavior;
    
*   timeouts and fallback rules;
    
*   connection and configuration details.
    
*   Authentication and authorization mechanisms
    

If one of these decisions changes, consumers don't necessarily need to change.

Maybe the key format moves from:

```text
sales-report:v3:{input-hash}
```

to:

```text
result:sales-report:v4:{input-hash}
```

That is an implementation detail of the memoization mechanism. It shouldn't become an implementation detail of every service using it.

The SDK creates a place for that knowledge to live.

## Making the expected path easier

Documentation describes how something should be used.

An SDK can make that usage the default.

This distinction becomes more useful as an architecture grows.

Suppose ten services need to call the same internal platform. We can give every team documentation explaining authentication, retries, headers, error mapping, timeouts, and endpoint conventions.

Or we can expose:

```typescript
const client = new PlatformClient(config);

const result = await client.execute(input);
```

Documentation is still useful. Consumers should understand the important behavior and tradeoffs.

They just shouldn't have to reproduce infrastructure code to use the platform correctly.

This also makes standardization less dependent on people remembering conventions.

If authentication changes, the SDK can change.

If every request should include new telemetry, the SDK can add it.

If a timeout policy needs to be adjusted, there is one implementation instead of several slightly different ones spread across the codebase.

## What belongs behind an SDK?

Not every abstraction needs its own package.

A small helper used twice is probably just a small helper.

An SDK becomes more interesting when several consumers interact with the same capability, and there are meaningful rules around that interaction.

Internal APIs are an obvious example, but the same idea can apply to caching, authentication, feature flags, event publishing, storage, business rules engines, or any other shared platform capability.

The important part is finding the right boundary.

An SDK that exposes every internal detail doesn't abstract much. At the other extreme, an SDK that tries to anticipate every possible use case can become more complicated than the system it hides.

A good SDK usually gives consumers the concepts they actually care about while keeping infrastructure decisions underneath.

In the memoization example, the consumer cares about generating the report.

It doesn't care how a deterministic input becomes a cache key.

That difference is a useful place to draw the boundary.

SDKs are therefore more than a way to avoid copying code.

Used at the right boundary, they let us implement an architectural decision once and give consumers a smaller, harder-to-misuse interface to it.
