What counts as a breaking change when your SDK is generated
A one-line spec edit can break every call site your users wrote. Here is the table of what is additive, what is breaking, and which ones your spec diff will not warn you about.

When you hand-write an SDK, you know when you have broken it, because you had to edit the method that broke.
When you generate one, the break happens somewhere between a pull request that looked harmless and a regenerated package that no longer compiles for the people who installed it. Nobody wrote the breaking change. It fell out of the spec.
The uncomfortable part is that a spec diff is a bad predictor of an SDK diff. Some edits that look enormous are additive. Some that look like nothing are breaking in every typed language you publish.
The table
| Spec change | Effect on the generated client |
|---|---|
| Add an endpoint | Additive |
| Add an optional parameter | Additive, unless the argument style is positional |
| Add a required parameter | Breaking. Every existing call site is now wrong |
| Rename a field | Breaking in typed languages, silent in untyped ones |
| Change a response type | Breaking in typed languages |
| Remove an endpoint | Breaking. The method disappears |
| Change an operationId | Breaking. The method is renamed |
Four of those seven produce no interesting diff in the document. A renamed field is one word. A changed operationId is one word. Both compile fine on your side, because you are not the one who wrote the call site.
The two that catch people
Adding a required parameter. This reads as an addition, and additions are supposed to be safe. It is not an addition to the caller. Every existing invocation of that method is now missing an argument it must supply, and in a typed language that is a compile error across your users' codebases at once.
If you need a new input, make it optional and give it a default on the server. If the operation genuinely cannot work without it, you are shipping a new operation and it should have its own id.
Renaming a field. In TypeScript, Go, Java, Rust and Swift, a renamed response field is a compile error, which is the good outcome: the failure lands at build time with a clear message.
In Python, Ruby, PHP and Dart it is not. The generated model changes shape, the caller's attribute access keeps parsing, and the failure arrives at runtime in whatever code path first touches the old name. If you publish to both kinds of language from one spec, your untyped consumers are the ones who find out in production.
The version does not compute itself
This is the part people assume works differently than it does, so it is worth being direct.
The package version comes from info.version in your specification. It is propagated into the generated manifests on every build, so the version on npm and the version in your document agree by construction.
info:
title: Acme API
version: 2.4.0Nothing infers a major bump from the diff. If you add a required parameter and leave info.version where it was, the package publishes with a version that says nothing happened. The table above is the input to a decision you make, not a decision the pipeline makes for you.
The corollary catches people too: editing the version inside a generated package.json or pyproject.toml does not survive. Those manifests are overwritten on every build. Change info.version in the spec.
Deprecate rather than remove
Removing an operation breaks callers at compile time, which sounds acceptable until you remember that the compile happens on their schedule and not yours. They upgrade for an unrelated reason, everything stops building, and now your removal is their incident.
Marking it deprecated instead surfaces the warning at compile time and keeps their code working. They get told, they get to plan, and you get to remove it in a release where removal is the headline rather than a surprise.
The generated client carries the deprecation through, so the notice appears where the method is used rather than in a changelog nobody read.
What to do before you publish
Read the diff as your users will experience it, not as your spec renders it. Three questions cover most releases.
Did any operation gain a required input, or lose an optional one it used to accept? That is a signature change in every language you publish.
Did any operationId change, including a change that looks like a typo fix? That is a method rename, and it is worth checking whether the tidy-up is worth the break. Overriding the generated method name for that endpoint keeps the public surface stable while the spec gets cleaned up.
Did any field in a response schema change name or type? Sort your published languages into typed and untyped, and decide which failure mode you are handing to which group.
Then set info.version to say what you concluded.
Where the SDK shape changes the answer
One row in that table has a qualifier, and it is worth pulling out: adding an optional parameter is additive only if the argument style is an object.
With an options object, a new optional field is invisible to existing calls. With positional arguments, adding a parameter shifts the position of everything after it, and a call site that compiled yesterday now passes the wrong value into the wrong slot. That is the worst class of break, because it can be type-correct and silently wrong.
This is why the generated clients promote a wide operation to a request object rather than growing an argument list, and why the argument style is worth a deliberate decision per language rather than an inherited default. The OpenAPI to Go SDK and OpenAPI to Java SDK pages show what each style produces at the call site.
Catch the ones that are already in your spec
Several rows in that table start as spec problems long before they become SDK problems. An operation with no declared response schema cannot tell you that its response type changed, because it never declared one. An operation with no operationId has a name that moves whenever its path does.
Our spec audit scores a document on exactly those grounds and names the operations responsible. It is free and it does not keep your spec. Run it before the release where one of these bites, rather than after.



