Generated-SDK break detection¶
apiverity breaking old.yaml new.yaml --sdk
A contract diff asks what happens to bytes on a socket. A generated SDK is a second contract, derived from the first by naming conventions, and it can break while the wire stays identical.
The one-line example¶
/users/{id}:
get:
- operationId: getUser
+ operationId: fetchUser
Nothing about the request or the response moves. Every rule in the catalogue is
silent, and apiverity diff reports no change at all — operationId is
compared by nothing, because nothing on the wire depends on it.
Every generated client's client.getUser(...) stops compiling.
The rules¶
| Rule | Change | Convention it assumes |
|---|---|---|
SDK-OPERATION-ID-CHANGED |
operationId renamed | operation-id-names |
SDK-OPERATION-ID-REMOVED |
operationId dropped | operation-id-names |
SDK-OPERATION-ID-ADDED |
operationId introduced | operation-id-names |
SDK-TAG-NAMESPACE-CHANGED |
first tag changed | tag-namespaces |
SDK-MODEL-NAME-CHANGED |
response schema title renamed |
title-model-names |
SDK-ENUM-VALUE-ADDED |
a response enum gained a value | closed-enums |
SDK-PARAMETER-ORDER-CHANGED |
required parameters reordered | positional-parameters |
apiverity explain SDK-OPERATION-ID-CHANGED gives the full entry for any of
them.
What this claims, and what it does not¶
Nothing here is tested against any particular generator. Each rule names the convention it rests on, and the assumption travels on the finding itself:
{
"rule_id": "SDK-TAG-NAMESPACE-CHANGED",
"metadata": {
"convention": "tag-namespaces",
"assumes": "operations are grouped into a namespace per tag"
}
}
| Convention | What a generator has to do |
|---|---|
operation-id-names |
method names derive from operationId |
tag-namespaces |
operations are grouped into a class or namespace per tag |
title-model-names |
model class names come from a schema's title |
closed-enums |
enums become closed types that reject unknown values |
positional-parameters |
required parameters become positional arguments |
These are what generators do, stated as assumptions rather than asserted as facts about tools this project has not run. If yours does not follow one, turn it off:
apiverity breaking old.yaml new.yaml --sdk \
--sdk-convention operation-id-names \
--sdk-convention closed-enums
Rules that depend on the conventions you left out do not run — they are not
downgraded, hidden or counted. And the artifact records what the run was
allowed to assume, under sdk_conventions, because a narrowed run and a full
one would otherwise read as the same run with fewer problems.
Why it is off by default¶
Two reasons, and only one of them is the conventions.
The other is severity. Nothing in this family is ever ERROR. These changes
are real and they are not wire-breaking, and a rule that outranks "you deleted
a required response field" because a class got renamed is a rule somebody
switches off along with everything near it.
The two verdicts that disagree¶
Adding a value to a response enum produces two findings, and they say different things:
BRK-ENUM-WIDENED— INFO. Additive. Nothing that was valid becomes invalid. True.SDK-ENUM-VALUE-ADDED— WARN. A client may now receive a value its generated type has no member for: a deserialization failure, or a match that is no longer exhaustive. Also true.
That disagreement is the point of the family. Where the two agree, there is
only one rule — a response field becoming nullable is
BRK-RESP-NULLABLE-ADDED and nothing else, because a second rule saying the
same thing in generator vocabulary is one finding printed twice.
The silent one¶
SDK-PARAMETER-ORDER-CHANGED is the only rule here whose failure is not a
compile error.
Swap two required parameters of the same type and a generator that emits them positionally produces a signature that still accepts every existing call site. The arguments arrive the other way round. Nothing fails to build; the wrong tenant gets read.
Its severity is WARN like the others, because severity is about what the gate should do, not about how much the failure deserves to be feared.