| name | writing-migration-guides |
| description | Use when a change is a breaking change — a `fix!:`/`feat!:` commit, a `BREAKING CHANGE:` footer, or any edit that changes a public API's shape or behavior — and needs an entry in the root `V<N>_MIGRATION.md` file. Also use when reviewing a PR that has one, to check it against this format.
|
Writing Migration Guides
Reference: supabase-flutter's MIGRATION.md.
It gets this right — read a section or two before writing your own if you want the tone.
Step 1: Confirm it needs an entry
Every breaking change needs one: a renamed or retyped public symbol, a changed default, a changed
error type, an event that fires differently, anything that a feat!:/fix!: commit or
BREAKING CHANGE: footer would announce. A deprecation alone does not — that gets an
@available(*, deprecated, ...) message instead (see Step 5).
Completion criterion: you can name the exact symbol or behavior that changed and why a caller
would need to touch their code because of it.
Step 2: Find the file, don't create a new one
supabase-swift keeps a single migration guide per upcoming major version, at the repo root,
covering every module:
V<N>_MIGRATION.md
<N> is the next major version above the latest release tag — check it with
git tag --sort=-v:refname | head -1; if the latest tag is v2.55.0, <N> is 3, so the file is
V3_MIGRATION.md.
If V<N>_MIGRATION.md already exists (because someone already landed a breaking change for the
upcoming major release), append a new ## section to it. Only create the file when none exists
yet for that version. Once v<N> ships, the file stays as the historical record for that release;
the next breaking change starts a new V<N+1>_MIGRATION.md.
This is unrelated to one-off feature-migration guides like docs/migrations/RealtimeV2 Migration Guide.md, which document moving from an old API to a new parallel one, not a version bump —
leave those as they are.
Completion criterion: you know the exact file path, and whether you are creating it or appending
to it.
Step 3: Write the section
Give the section a ## heading naming the exact symbol or behavior, in backticks:
## `AuthResponse.user` is now optional
Then, in order:
-
What changed. One or two sentences. Name the concrete types/names involved — not "the API
changed" but "AuthResponse.user is User? instead of User".
-
Why. The concrete cause: a bug that surfaced, a shape the server actually sends, an
inconsistency with another Supabase SDK. Never "for consistency" or "to improve the API" on
their own — say what broke or what it now matches.
-
Before/After code. Real call-site Swift, not pseudocode:
let email = response.user.email
let email = response.user?.email
-
Compile error or silent? State this explicitly. If the change is a type change that Swift's
compiler will catch (like the example above), say so — the reader can grep for the compiler's
error list. If it is a behavior change that still compiles (a default flipping, an event firing
under a different name), say so too, and tell the reader what to search their codebase for.
-
Escape hatch, if any. If the old behavior is still reachable (an explicit parameter, a
different call), show it.
Since the file covers every module, only name the module explicitly when it isn't obvious from the
symbol itself — AuthResponse and RealtimeClient don't need it, but a change to something
module-agnostic-sounding like HTTPMethod does.
When a change enumerates more than two or three renames or replacements, use a table instead of
prose:
| Before | After |
| --- | --- |
| `RealtimeClient.conn` | `RealtimeClient.connection` |
| `RealtimeClient.connState` | `RealtimeClient.connectionState` |
Completion criterion: the section has What/Why/Before-After, and explicitly says whether the
break is a compile error.
Step 4: Order and grouping within the file
If the file already has sections for other changes targeting the same version, add yours as a new
## section rather than folding it into an existing one, even if the topic feels related. One
section per distinct breaking change keeps each one independently linkable.
Step 5: Cross-link and verify
-
Link the guide from the PR description.
-
If this breaking change comes with a deprecated fallback (the old API keeps working but is
marked @available(*, deprecated, ...)), point its message at the guide using the pattern
already used in Sources/Realtime/Deprecated/*.swift:
@available(*, deprecated, message: "Use X instead. See migration guide: https://github.com/supabase-community/supabase-swift/blob/main/V<N>_MIGRATION.md")
-
Run ./scripts/spell-check.sh — migration guides are Markdown and get spell-checked with
everything else. Add project-specific terms to dictionary.txt if it flags something legitimate.
Completion criterion: ./scripts/spell-check.sh passes, and the guide is named in the PR
description.
Common mistakes
| Mistake | Fix |
|---|
| Creating a new file per breaking change | Append a ## section to the existing V<N>_MIGRATION.md |
Creating a per-module file (e.g. docs/migrations/Auth Migration Guide.md) | One root file per major version, covering every module |
| "For consistency" / "to improve the API" as the whole reason | Name the concrete cause: a bug, a server behavior, a mismatch with another SDK |
| Pseudocode in Before/After | Use real, compiling Swift from an actual call site |
| Not saying whether it's a compile error | Always call it out — readers need to know whether their build will catch it or not |
| Prose listing many renames | Use a | Before | After | table |
Renaming an unrelated one-off guide (e.g. docs/migrations/RealtimeV2 Migration Guide.md) to fit the V<N>_MIGRATION.md pattern | Leave feature-migration guides alone; the versioned pattern is only for major-version breaking changes |