On this page

Compatibility

Seagull is in alpha. Until v1, any release may change the public API, but each change is deliberate and listed in the notes of the release that makes it. From v1 on, the guarantees below apply.

Stability tiers

The public surface is the root package, seagull.sh/seagull, and the kit under sdk/, which has one area for each extension point: sdk/dialect, sdk/emit, sdk/migrate, sdk/schema and sdk/observe. Each area is a facade that names only what a program uses or implements: its contracts, named ...Port, and the values that cross them. Its providers are sub-packages named <area>_<role>_<impl>, such as sdk/dialect/dialect_engine_postgres. The implementation lives under internal/, which no program imports, so a name the facades leave out carries no promise.

TierPackagesPromise
Stableseagull.sh/seagull (the root facade, with its diagnostic codes), sdk/observe, sdk/migrate (apart from the executor ports and lexical rule types below) and its migrate_backend_* providers, cmd/sgl/config, sdk/dialect/dialect_registry (whose Options may gain fields), the dialect.Dialect handle, and each dialect_engine_* and emit_driver_* provider's New function with its optionsChanges only in a new major version.
Evolvingsdk/dialect's Facts and its schema and type model, sdk/emit (the emitter ports and the query and Go-type model), sdk/emit/emit_kit, sdk/emit/emit_testkit, sdk/schema and its schema_source_postgres and schema_source_sqlite providers, cmd/sgl/cli, cmd/sgl/drivers, cmd/sgl/assemble, cmd/sgl/starter, the lexical rule types of sdk/migrate (Rules and its companions), and the sdk/migrate executor ports: ExecutorPort, SeedExecutorPort, the optional HistoryEditorPort and SeedLockTrierPort, MigrationRecord and AppliedMigrationMay gain fields and methods in minor versions; existing names keep their meaning. Code that implements an emitter, a schema source or a migration executor may need small updates between minor versions.
InternalEverything under an internal/ directory, the implementation behind the root facade and the sdk/ areasNo promise.

Dialects

sdk/dialect has no port to implement. A dialect.Dialect is an opaque handle to one of the built-in engines, which a program configures through the engine provider's options or dialect_registry.Options; dialects written outside Seagull are not supported yet. A Facts value may gain fields in a minor version, as a new kind of fact reaches generated code.

A dialect's options apply in the order they are given, after the defaults of a flavour such as CockroachDB, and each sets one value, the last one given winning: dialect_engine_cockroachdb.New(dialect_engine_cockroachdb.WithSearchPath("app")) keeps CockroachDB's own settings and resolves unqualified names in the app schema. A new option arrives in a minor version with a default that leaves existing output unchanged; an option that changes generated code is opt-in, or its change is listed in the release notes.

Migration executors

sdk/migrate drives any ExecutorPort, so a third-party engine gets the migration runner by implementing that port, or by describing its SQL to migrate.CustomBackend(spec), which splits statements with the engine's lexical migrate.Rules. The contract, what each method must do under the lock and how a resumed migration is checked, is in the migrate API. The optional ports HistoryEditorPort (behind Force and Repair) and SeedLockTrierPort (behind non-blocking seed locks) are found by a type assertion; an executor without them gets a clear error from the operations that need them, never a silent no-op.

Emitter packages

The emitter interface, emit.EmitterPort, has one method so that it never has to change; the optional TypeDefaulterPort, ReserverPort, CheckerPort and NullElementBinderPort keep theirs. Both sdk/emit and emit_kit are Evolving rather than Stable because they grow with code generation: a new query shape brings new names to the naming plan (emit.QueryNaming), new places to the rendered SQL (emit_kit.Marks) and new choices to the renderer (emit_kit.Renderer). These arrive as new fields and functions, and an existing name keeps its meaning, so an emitter that writes keyed struct literals and reads the fields it knows keeps compiling; it may need a small change to take part in a feature it did not know about.

emit_testkit is Evolving too, and a minor version may add a check, as a new class of bug is found and guarded against for every emitter. An emitter that fails a new check records it in Suite.KnownFailures, with the reason, until it is fixed. The martian dialect that emit_testkit.Run adds to every suite, an invented engine that shares no convention with a real database, may likewise widen its grammar or change what it reports in a minor version, as a new guard against a reference engine needs it.

Diagnostic codes

Diagnostic codes (Q001 and up) never change meaning and are never reused. A code that is retired leaves a gap.

Generated code

seagull.CodegenVersion changes whenever identical input would produce different Go. Tools that check generated code into a repository can compare it to decide whether a regeneration is due. The generated-file header names the tool: // Code generated by sgl. DO NOT EDIT.

Persisted names

The tables, constraints and lock keys a migration backend uses (Backend.Names) are part of the stable surface: a release that changes them would leave existing deployments with a second history. They change only with a migration path and a release note. MySQL lock keys are scoped to the current schema; earlier builds, and Piko's own runner, used server-wide keys, which migrate_backend_mysql.WithServerWideLockKey keeps so that old and new processes take the same lock during a rolling upgrade.

The migration history table's original eight columns never change. Columns added since (splitter_version and statements_hash, which let an interrupted migration resume safely) are added by the first Up that holds the lock, to tables created by earlier releases as well as new ones; earlier releases ignore them.

Configuration

sgl.yaml carries a version field, which is 1 today. A new schema version is introduced only alongside a migration path, and sgl config validate, like every command that reads the file, rejects a version the sgl running it does not read, naming the version it does.

History

Seagull began as the SQL code generator inside the Piko web framework, where its directives used the piko. prefix and its tables the piko_ prefix. Piko now consumes Seagull through a bridge that keeps its own vocabulary, so existing Piko projects see no change.