Help us shape the future of Go! Take the 2026 Go Developer Survey

compat

package
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 29, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Overview

Package compat reads the contracts a migration declares about itself: that it is BACKWARD-COMPATIBLE, or deliberately BREAKS ROLLBACK (this file), and which foreign migration history a baseline ADOPTS (adopt.go).

THE CONTRACT. A migration's .up.sql may carry the line

-- forge:backward-compatible

which is its author's statement that the PREVIOUS release's code keeps working against the schema this migration leaves behind — the "expand" half of expand/contract. An ADD COLUMN that old code never reads is backward-compatible; a DROP COLUMN old code still SELECTs is not.

WHY A DECLARATION AND NOT AN INFERENCE. Whether N-1 code survives a schema is a fact about that code, not about the SQL, and nothing short of running it can prove it. An inference from the SQL would be wrong in both directions: `DROP TABLE` of a table nothing reads is safe, and `ADD COLUMN ... NOT NULL` without a default breaks every old INSERT. So the author, who knows, states it once, in the file, in review — and forge enforces what follows from it: the migrator records the declared versions in the database as it applies them, so an OLDER binary that meets a schema ahead of it — the previous release's pods, still serving or rescheduled, during every rolling deploy — can prove the extra versions are safe rather than hoping (see migratekit.SchemaAheadError).

WHY THIS IS ITS OWN PACKAGE. It is a comment parser every project's migrator links; keeping it free of golang-migrate lets any tool read the declaration without that dependency, so it imports nothing but the standard library.

Index

Constants

View Source
const AdoptsDirective = "-- forge:adopts"

AdoptsDirective is the line a baseline carries to declare the history it replaces, followed by the tool and its versions. Exported so the linter, errors and docs quote the spelling the parser matches.

View Source
const BreaksRollbackDirective = "-- forge:breaks-rollback"

BreaksRollbackDirective is the line a migration carries to declare the opposite: the PREVIOUS release does NOT keep working on the schema this migration leaves — a dropped or renamed column old code reads, a NOT NULL column old code does not write. It takes a reason on the same line:

-- forge:breaks-rollback the 2026-09 release stopped reading plans.legacy_tier

The migrator treats it exactly as it treats a migration that declares nothing (an older binary refuses to serve over it). What it adds is that the author SAID so, in review, with why: a project that requires every migration to declare itself (`database.migration_safety.require_compat_declaration`) can tell a deliberate break from a forgotten declaration.

View Source
const Directive = "-- forge:backward-compatible"

Directive is the exact line a migration carries to declare itself backward-compatible. Exported so remediation text, the linter and the scaffolded comment all quote the same spelling the parser matches.

View Source
const ToolForge = "forge"

ToolForge names forge's own migrator: a history `forge db squash` replaced, recorded in schema_migrations and schema_migrations_applied.

View Source
const ToolGoose = "goose"

ToolGoose names pressly/goose, whose history lives in goose_db_version.

Variables

This section is empty.

Functions

func Declared

func Declared(upSQL string) bool

Declared reports whether one migration's up SQL declares itself backward-compatible. A file that ALSO declares breaks-rollback contradicts itself, and is read the safe way: not compatible, so an older binary refuses to serve over it rather than trusting half of the claim.

Types

type Adoption

type Adoption struct {
	// Tool is the migration tool whose history this replaces (ToolGoose).
	Tool string
	// Versions is every version that tool recorded for the history this
	// migration replaces: ascending, distinct, each positive.
	Versions []int64
	// Line is the 1-based line of the first directive, for messages.
	Line int
}

Adoption is the foreign history one migration declares it replaces.

func ParseAdoption

func ParseAdoption(upSQL string) (*Adoption, error)

ParseAdoption reads the adoption one migration's up SQL declares. No directive is (nil, nil): almost every migration adopts nothing.

The grammar is strict, and deliberately so. Every database the binary meets parses it at boot — a fresh test database included — so a malformed list fails the first CI run instead of waiting for the one production database it was written for.

type AdoptionSyntaxError

type AdoptionSyntaxError struct {
	Line   int
	Reason string
}

AdoptionSyntaxError is a malformed adoption directive. It carries the line so the linter can point at it; the migrator refuses the whole migration set, because a baseline whose adoption cannot be read would run its SQL against a database that already has the schema.

func (*AdoptionSyntaxError) Error

func (e *AdoptionSyntaxError) Error() string

type Declaration added in v0.1.46

type Declaration struct {
	// Line is the 1-based line it is on.
	Line int
	// Reason is the text after the directive, with the separator a reader
	// types (`—`, `-`, `:`) trimmed; "" when there is none. A reason that
	// starts with `<` is a template placeholder (`<why …>`), not a reason,
	// and reads as "".
	Reason string
}

Declaration is one compatibility directive line in a migration.

func Declarations added in v0.1.46

func Declarations(upSQL string) (compatible, breaksRollback []Declaration)

Declarations returns every backward-compatible and every breaks-rollback line in one migration's up SQL. A file is meant to carry exactly one of the two; the linter reports anything else.

type Migration

type Migration struct {
	// Version is the numeric prefix — the value golang-migrate records in
	// schema_migrations.
	Version uint
	// Name is the filename, for messages.
	Name string
	// Descriptor is the filename between the version prefix and `.up.` —
	// `retire_plan_compute_free` in `00091_retire_plan_compute_free.up.sql`.
	// It is what identifies WHICH migration a version is: two branches that
	// both claim 91 differ here, while a change of zero-padding does not.
	Descriptor string
	// BackwardCompatible is whether the file carries Directive (and not
	// BreaksRollbackDirective as well; see Declared).
	BackwardCompatible bool
	// BreaksRollback is whether the file carries BreaksRollbackDirective.
	BreaksRollback bool
	// Adopts is the foreign history this migration replaces, from its
	// AdoptsDirective lines; nil when it declares none. See adopt.go.
	Adopts *Adoption
}

Migration is one up migration's version and what it declares.

func Scan

func Scan(fsys fs.FS, dir string) ([]Migration, error)

Scan reads every up migration in dir of fsys, in version order.

A directory holding no up migrations returns an empty slice, not an error: a project that has not written one yet is a normal state. An unreadable directory IS an error, because "no migrations" and "could not look" call for different fixes.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL