> For the complete documentation index, see [llms.txt](/llms.txt)

# Breaking-change detection

A spec change that looks small on the wire can delete a method from thirteen SDKs. Renaming an
`operationId`, tightening a type, making an optional parameter required — each one compiles fine on
your side and breaks every caller who upgrades.

So Glotto classifies the change before you merge it. Every pull request that touches your spec gets
a **preview build**, and that build carries an API-surface comparison against the last one: what
changed, and for each change, whether a caller who upgrades keeps working.

## What you get

On the pull request, in the preview-build comment:

| Target | Status | Files | Changes vs. base | API surface |
| --- | --- | --- | --- | --- |
| `typescript` | ✅ built | 42 | 3 changed | ⚠️ 1 breaking |
| `python` | ✅ built | 39 | 3 changed | ⚠️ 1 breaking |

The **API surface** column is the classification. `⚠️ N breaking` means at least one change would
break a caller who upgrades; `N non-breaking` means the surface moved but nobody's code stops
working; and a clean comparison says so rather than staying silent.

Expanding the section beneath the table lists every change, **breaking first**, with the operation
or model it belongs to. That ordering is the point: the breaking ones are the whole reason to read
the list, and they are what a reviewer needs before approving.

## What counts as breaking

The classification is about **your callers**, not about the diff:

- **Breaking** — a method or model disappears, is renamed, or changes shape in a way an existing
  call site cannot survive. A removed operation, a renamed `operationId`, a parameter that becomes
  required, a response field that changes type.
- **Non-breaking** — the surface grew or was refined without invalidating existing code. A new
  operation, a new optional parameter, a new response field, a widened type.

Two things are deliberately **not** breaking, because they are the mechanisms for not breaking:

- **An [alias](/docs/glotto-yml-api-surface#aliases--deprecated).** Renaming `createRecord` to `upsertRecord`
  and declaring `aliases: { createRecord: upsertRecord }` keeps the old method name in every SDK,
  routed to the new operation — so the rename re-occupies the surface it vacated, and the
  classification says non-breaking. That is the honest answer: no caller breaks.
- **A [transform](/docs/transforms).** Corrections you apply to the spec before generation are part
  of the input, so the comparison sees the corrected surface on both sides.

## What it compares against

**The previous build of the same target.** Glotto stores the spec revision every preview build was
generated from, and the next build classifies against it — so the comparison always describes the
change *this* pull request makes, not the accumulated drift since some fixed point.

Two consequences worth knowing rather than discovering:

- **A target with no previous build has nothing to compare against**, so its first preview reports
  no API surface rather than reporting everything as new. A brand-new target is not a breaking
  change to a caller who does not have it yet.
- **Targets on different baselines get their own sections.** A target added later has a different
  last-build than its siblings, and merging the two comparisons would produce a diff that
  accurately describes neither.

## Making it impossible to miss

```yaml
# glotto.yml
settings:
  detect_breaking_changes: true
```

With this set, a breaking change is called out **at the top of the preview comment**, naming how
many there are, instead of living in one table cell and a collapsed section. Leave it unset and the
classification is still there — it is just quieter.

Set it once your first release is out and callers exist. Before that, everything is breaking and
nothing is.

**This is emphasis, not enforcement.** Glotto does not set a commit status or a check run on your
pull request, so nothing here can be made a required check and nothing blocks a merge. If you want
that, make it a branch protection rule on your own side; what Glotto guarantees is that the
information is on the pull request, correct, and impossible to overlook.

## What this does not do

- **It does not decide your version number.** The classification is an input to that decision, not
  a substitute — a breaking change with an intentional major bump is a normal release, and Glotto
  does not guess which one you meant.
- **It does not compare against an arbitrary point in history.** The baseline is the last build, by
  design: a comparison against a ref you name would answer a different question, and answering it
  well needs a route we do not offer today.
- **It does not classify behaviour, only surface.** An operation that keeps its signature and
  changes what it returns at runtime is invisible here. That is what your
  [contract tests](/docs/verification-report#real-compile--contract-statuses-the-checks-loop) are
  for.
