Decide between generating and hand-writing a client
Count the operations, schemas and parameters you would maintain, then decide between a generated client and one you write on the numbers rather than on taste.
How-to › Section 6
Choosing how to produce client libraries, generating them, making them idiomatic, composing runtime features, customizing a generator without forking, adding languages, and regenerating safely.
23 guides in 6 topics. Every guide compares its approaches and shows the output its commands printed.
Deciding and doing: a repeatable rubric for hand-written versus generated versus AI-written versus SaaS-generated SDKs (total cost of ownership, per-seat pricing, determinism measured across two runs, entity-shaped versus endpoint-shaped output measured rather than asserted), then running OpenAPI Generator, Swagger Codegen, Kiota, Speakeasy, Stainless, Fern, liblab, oapi-codegen, openapi-typescript with openapi-fetch, orval, Hey API, openapi-python-client, NSwag, AutoRest, Smithy, and sdkgen (npm create @voxgig/sdkgen, target add, --only, --dryrun), judging the output (the voxgig-solardemo-sdk Go reference and the elementdemo repo as things to read before choosing), and migrating from one generator to another while keeping package names and semver continuity.
Count the operations, schemas and parameters you would maintain, then decide between a generated client and one you write on the numbers rather than on taste.
Set up a Fern project from an OpenAPI document, read the generators.yml it writes, validate it offline, and know which generator group runs before anything runs.
Join one document per service under a prefix rule fixed before the first merge, generate one package from the result, and test that no name moves.
Scaffold a project from your OpenAPI document with one non-interactive command, generate a TypeScript SDK, and read what the model made of your endpoints.
Count the nouns, the operations per noun, and how many names start with a verb, so a claim about SDK shape is a measurement rather than a preference.
What makes an SDK feel native: entities versus endpoint wrappers, constructor and configuration, method naming, options objects versus builders, sync and async variants, cancellation (context.Context, AbortSignal), typed errors, nullable handling, response envelopes, ESM and CJS dual builds, and escape hatches for raw requests (sdkgen direct and prepare, Stainless raw responses, OpenAPI Generator withHttpInfo).
Type a PHP SDK against the PSR-18 and PSR-17 interfaces, let callers pass the HTTP client they already run, and fail with an install command when none is present.
Turn HTTP failures into a small error hierarchy carrying status, a stable code, the request id, and the raw body, and test that each status maps to one type.
Give a Ruby client keyword-argument methods and Data response objects, let callers add Faraday middleware, and prove a wrong keyword raises before any request.
Write the client once as coroutines over httpx, generate the sync twin with unasync at build time, and pin both to one method set with a test.
Decide the home entity of every operation before you pick a generator, test the three documented call chains, and see what two generators guess.
How cross-cutting behaviors fit together inside one client, and the smaller behaviors a client carries: permission hints, proxy routing, debug buffers, structured logs, request tracking, audit records and cost attribution.
Place each cross-cutting behavior at the sdkgen stage that holds its input, and prove with forced failures which stages a call runs and which it skips.
Send three identifiers with three lifetimes, so a support question about one call can be answered from the service's own logs.
Compose retry, caching, authentication and tracing as layers around one transport, so each concern is written once and the order is declared rather than implied.
Write one record per logical SDK call, naming the caller, the operation, the entity, and the outcome, to an append-only sink, and never one record per HTTP attempt.
Bending generated output without a fork: OpenAPI Generator custom templates and .openapi-generator-ignore, Speakeasy overlays and hooks, Fern custom code, Kiota, and the six sdkgen levers (model .aon files with project.aon never overwritten, templates in .sdk/tm, TypeScript components in .sdk/src/cmp, custom features with per-stage hooks, custom targets, and sdkgen packages with package add, check, list, update), plus docs add, the flags --only, --alias, --force, --dryrun and --debug, typed models, doctor in CI, installing a third-party sdkgen package such as @voxgig/sdkgen-haskell or sdkgen-station, propagating template changes, migrating a bundled target out of tree, driving generation from a script with the programmatic API, and debugging generation.
Record what the generator shipped, compare the project against it on every build, and separate a file you forked from a file you added.
Read the FILES manifest for what OpenAPI Generator wrote, search its extracted template set for the file's text, and prove the match with a one-line marker.
Write a client's computed parts, a dispatch table and an overload matrix, as a function over the typed model, and hold its output to a formatted fixture.
Turn a feature you copy between sdkgen projects into a versioned package, install it into two projects, and roll both forward while each keeps its own settings.
Rename a generated method where its name is decided, in the OpenAPI document or in the generator, regenerate, and prove the rename reached every target.
Adding a language to an SDK programme: how to grade targets yourself when the generator does not (sdkgen publishes 22 bundled targets plus package-delivered ones such as Haskell, but grades none of them; OpenAPI Generator labels each generator's stability; Speakeasy publishes a supported-language list), what less polished should mean to you, packaging expectations per ecosystem, and authoring a new target (sdkgen author-a-new-language, OpenAPI Generator new-generator scaffolding, the Fern generator API).
Rank candidate SDK languages from tickets, signup answers, registry counts, and the User-Agents of raw API calls, the source that sees users you have not reached.
Run one fixed evaluation on a candidate language target, record what you measured beside the generator's claims, and decide who supports it before it ships.
Write each SDK language's tier as data, generate the CI matrix and the published support table from it, and fail any release that broke a tier's promise.
Regeneration as a routine operation: three-way merge against the last generation, preserve and protect markers, diff mode, overwrite policies, reviewing a regeneration diff, failing CI when a regenerated SDK differs from the committed one, vendoring and upgrade migration, keeping hand edits in separate files.
See exactly what a generator would change before it changes anything, with a dry run that reports creates, replacements, and the files its mode protects.
Retries, timeouts, pagination and auth are the same problems in every client. Voxgig generates them from your OpenAPI description, in 23 languages, from one model.
Since 2018, Richard Rodger has talked with developer advocates, event organizers, and the people who build developer-facing companies about the specifics of the work.