# Backend migrations

Goose (`pressly/goose/v3`) migrations embedded into the backend binary
by `embed.go`. Applied at startup before any listener opens — see
`internal/postgres`.

## Authoring conventions

- Each schema change is a new file with a monotonically increasing
  numeric prefix and a snake-case slug:
  `0000N_short_description.sql`. Reuse of a prefix is forbidden once
  the file is merged.
- `00001_init.sql` is the historical baseline. Treat it as immutable
  history; do not edit it to land new schema. Squashing the chain back
  into a fresh `00001` is reserved for the explicit pre-production
  cut-over.
- Every file MUST contain both an `-- +goose Up` and `-- +goose Down`
  section, even if Down is a single `DROP …` for the same artefacts.
  Down migrations are exercised by the schema test and serve as the
  documented rollback path.
- Destructive changes (dropping columns/tables, renaming with data
  loss) MUST be split into at least two migrations so the chain stays
  rollable forward and backward without coordinated code+schema
  windows:
  1. add the new shape, dual-write the data, leave the old shape in
     place;
  2. once all readers have switched, drop the old shape in a follow-up
     migration.
- Migrations are applied automatically on backend startup, so a fresh
  push to `development` plus the `dev-deploy.yaml` workflow brings the
  long-lived dev database up to head without manual intervention.
  `make -C tools/dev-deploy clean-data` is only needed when a developer
  deliberately wants a fresh database.
- The integration harness (`backend/internal/postgres/migrations_test.go`)
  spins up a disposable Postgres per run and asserts the final table
  set. When a migration adds or removes tables, update the expected
  list in the same patch.

## Pre-production squash

The chain may be squashed back into one clean `00001_init.sql` before
the first production deployment. That is a deliberate, one-time
operation; until then, additive numbered files are the rule. After the
squash this file gets a short note that `00001_init.sql` represents
the production baseline and the policy above continues to apply for
every later migration.
