SDK generation

The real maintenance cost of a hand-written API client

A hand-written API client costs more than the afternoon it took to write it — it costs an edit every time the API adds an endpoint, changes a parameter, or rotates an auth scheme, multiplied by every language you maintain a client in. For a single internal consumer and a small, stable API, that ongoing cost is genuinely low and hand-writing is still the right call. For a public API with external customers and more than one language, the arithmetic flips fast, and this post is the accounting that shows where.

What actually forces an edit

A hand-written client isn't a one-time cost because the API underneath it doesn't hold still. Four categories of change force a human to open the client's source and edit it:

New endpoints. Every new route your API ships is a method someone has to write by hand in the client — request shape, response type, error handling, and (if you're disciplined) a test. Miss one and the client is incomplete without anyone realizing until a consumer needs it.

New or changed parameters. A new optional query param is a small edit. A parameter that becomes required, or one whose type changes, is a bigger edit — it touches every call site in the client and potentially every call site in every consumer of the client.

Auth changes. Rotating from API keys to OAuth, adding a new required header, or changing token refresh behavior touches the client's core request logic, not just one method. This is the category most likely to silently break every existing integration at once if the edit is wrong.

Response shape changes. A field renamed, a field's type narrowed, a nested object flattened — these ripple through every type definition and every consumer that destructures the response. This is also the category hardest to catch by manual review, because nothing about the code looks wrong until a caller hits the new shape at runtime.

The cost multiplies per language

Each of the four change categories above isn't a single edit — it's one edit per language you maintain a client in. A new endpoint means writing the method in TypeScript, then again in Python, then again in Go, each with that language's idioms for async, error types, and pagination. A parameter type change means finding and fixing every call site, in every language, and every language's type system enforces (or fails to enforce) that fix differently — a Python client with no static typing can silently accept the wrong shape in a way a TypeScript client's compiler would catch immediately.

This is what hand-maintenance estimates usually get wrong: teams budget "update the client" as one task, when it's N tasks where N is your language count. A team maintaining TypeScript, Python, and Go clients is doing roughly 3x the work of a TypeScript-only team, and that multiplier applies to every API change, not just the big ones.

Change type Single-language cost Three-language cost
New endpoint One method, one test Three methods, three tests, three sets of language idioms
Required param added Update call sites in one client Update call sites in three clients, three type systems
Auth scheme change Rewrite request core once Rewrite request core three times, verify token handling per language
Response field renamed Update types, find call sites Update types three times; untyped languages may not catch it until runtime

When hand-writing is still the right call

None of this means every API needs a generation pipeline. Hand-writing is still the right call when:

  • You have one internal consumer. If the only thing calling your API is your own frontend, on a codebase you control, a breaking change is a same-day fix by the same team that made the API change. There's no external customer discovering the break days later.
  • The API is small and stable. A handful of endpoints that rarely change means the four cost categories above rarely trigger. The maintenance cost of a five-endpoint internal API is close to zero regardless of how the client was built.
  • You only need one language. The multiplier that makes hand-writing expensive doesn't apply if there's genuinely only one consumer language and no plan to add a second.

If all three are true, don't add a generation pipeline you don't need — see the SDK drift post for what that pipeline is actually solving, and skip it if the problem it solves isn't yours yet.

When the math tips over

The tipping point is usually one of three signals, and they tend to arrive together:

  1. A second language request arrives. The moment a customer or internal team asks for a client in a language you don't maintain, you're choosing between writing a second hand-maintained client (multiplying your cost) or generating one. See what to do when customers ask for a Python SDK for that decision in detail.
  2. External customers start depending on the client. Once someone outside your org has the client in their lock file, a breaking change becomes a support incident instead of a same-day internal fix. The cost of getting it wrong goes up even if the cost of maintaining it doesn't.
  3. API change frequency crosses roughly weekly. Below that, a human can keep up by hand without much friction. Above it, edits start queuing, and the gap between "API shipped" and "client caught up" starts to look like the drift problem in your SDK is three versions behind your API.

What generation actually replaces

Generating a client from your OpenAPI spec doesn't remove the four change categories — the API still adds endpoints, still changes parameters, still rotates auth. What it removes is the manual multiplication: one spec change produces an updated client in every generated language automatically, instead of N manual edits someone has to remember to make. Sourced's SDK generator does this for TypeScript and Python today — the two ecosystems where teams publish first — from the same spec push, with a compatibility report attached so you can see what changed before it ships.

You can create hosted docs and an SDK preview from your existing repo free, with no credit card, and compare the generated diff against what your team would have edited by hand for the same spec change.

FAQ

How much does a hand-written API client actually cost per year?

It depends entirely on API change frequency and language count. A stable, single-language, single-consumer API can cost near zero. A public API changing weekly across three languages can cost multiple engineer-days per month once you total endpoint additions, parameter changes, and response-shape fixes across every client.

Is a generated client always cheaper than a hand-written one?

Not for a small, stable, single-consumer API — the generation pipeline itself has a setup cost that isn't worth paying if the API rarely changes. It becomes cheaper once you're paying the per-language multiplier described above on a regular basis.

What's the single biggest hidden cost in hand-maintained clients?

Response shape changes in untyped or loosely typed languages. A TypeScript compiler often catches a shape mismatch at build time; a Python client without strict typing can pass a changed shape straight through to a customer's runtime error.

Can I generate some languages and hand-write others?

Yes, and many teams do — generate the languages with the most consumers or churn, and hand-write a rarely-touched internal client. Be explicit about which is which so nobody assumes parity that doesn't exist.

Does switching to a generated client remove all maintenance work?

No — you still review generated diffs before publishing, especially for breaking changes. See SDK compatibility reports, explained for what that review looks like.

How do I estimate my own multiplier before switching?

Count your active client languages and average weekly spec changes last quarter (an OpenAPI changelog helps). Multiply the two — that's roughly how many manual client edits your team makes per quarter today.