API SDK design
An SDK is the experience most developers have of your API; a good one makes integration a pleasure and a bad one sends them to a competitor. It must feel native in each target language, handle the hard parts (auth, retries, pagination) invisibly, and evolve without breaking the apps built on it.
Method
- Feel idiomatic in each language. An SDK should read like code written for that language: Pythonic in Python, idiomatic Go in Go (see kotlin-idioms, go-project-layout for what "idiomatic" means per language), naming and patterns matching the ecosystem's conventions. A thin transliteration of one language's SDK into another (Java-style getters in Python) reads as foreign and fights the user's instincts.
- Handle auth and resilience so users do not. Token management and refresh (see oauth-flows), retries with backoff, timeouts, connection pooling, and rate-limit respect built in with sensible defaults (see api-client-design: the SDK is the definitive client): the user configures credentials and gets a robust integration. Making users implement resilience themselves guarantees most integrations do it badly.
- Make the common case one obvious line, the rare case possible. The 90% use case (create a resource, list with pagination) is trivial and discoverable (auto-complete-friendly, see api-surface-minimalism); advanced control (custom retries, raw response access, timeouts per call) is available without cluttering the simple path. Progressive disclosure: simple by default, powerful when needed.
- Design types and errors for the language's tools. Typed models (so IDEs autocomplete and type-checkers catch mistakes: see type-safety), typed errors matching the language's error idiom (exceptions in Python/Java, Result/error returns in Go/Rust: see rust-error-handling, api-error-responses), and null/optional handling that fits the language (see null-handling). The SDK's types are a large part of its usability.
- Version the SDK with clear compatibility. Semantic versioning (see release-tagging), backward-compatible evolution (additive: see api-change-management), and a clear policy for breaking changes with migration guides (see api-deprecation): apps depend on your SDK, so a careless breaking change breaks them all. Decouple SDK version from API version thoughtfully (an SDK may support multiple API versions).
- Choose generated, hand-written, or hybrid deliberately. Generated SDKs (from OpenAPI/proto: see openapi-contracts) scale to many languages and stay in sync with the API but can feel mechanical; hand-written feel better but cost per language; hybrid (generated core, hand-written ergonomic layer) is common. Match the choice to how many languages you support and how much polish the audience expects.
Boundaries
- SDK quality directly affects adoption (developer marketing: see developer-marketing); it is worth real investment for a public API, less for an internal one with a captive audience. Match effort to reach.
- The SDK is documentation's most-used form (developers read the SDK's methods and types more than the prose docs); good naming and types are documentation (see api-reference-docs, naming-things).
- Maintaining SDKs across many languages is a real ongoing cost (each needs updates per API change, testing, releases); generation reduces but does not eliminate it. Budget for the languages you commit to.