Files
bible/rules/patterns/go-database/contract.md
2026-04-02 13:48:36 +03:00

3.6 KiB

Contract: Database Patterns (Go / MySQL / MariaDB)

Version: 1.9

See README.md for examples, migration snippets, and Docker test commands.

Query and Startup Rules

  • Never execute SQL on the same transaction while iterating an open result cursor. Use a two-phase flow: read all rows, close the cursor, then execute writes.
  • This rule applies to database/sql, GORM transactions, and any repository call made while another cursor in the same transaction is still open.
  • User-visible records use soft delete or archive flags. Do not hard-delete records with history or foreign-key references.
  • Archive operations must be reversible from the UI.
  • Use gorm:"-" only for fields that must be ignored entirely. Use gorm:"-:migration" for fields populated by queries but excluded from migrations.
  • Always verify the DB connection before starting the HTTP server. Never serve traffic with an unverified DB connection.
  • Prevent N+1 queries. Do not query inside loops over rows from another query; use JOINs or batched IN (...) queries.

Migration and Backup Rules

  • The migration engine owns backup creation. The operator must never be required to take a manual pre-migration backup.
  • Backup storage, retention, archive format, and restore-readiness must follow backup-management.
  • Before applying any unapplied migrations, take and verify a full DB backup.
  • Before applying a migration step that changes a table, take a targeted backup of each affected table.
  • Before writing any backup, verify that the output path resolves outside the git worktree and is not tracked or staged in git.
  • If any migration step in a session fails, roll back all steps applied in that session in reverse order.
  • If rollback is not sufficient, restore from the targeted backup taken before the failing step.
  • After rollback or restore, the DB must be back in the same state it had before the session started.
  • Migration failures must emit structured diagnostics naming the failed step, rollback actions, and final DB state.

Migration Authoring Rules

  • For local-first desktop apps, migration recovery must also follow local-first-recovery.
  • Migrations are sequential and immutable after merge.
  • Each migration should be reversible where possible.
  • Do not rename a column in one step. Add new, backfill, and drop old across separate deploys.
  • Auto-apply on startup is allowed for internal tools only if the behavior is documented.
  • Every .sql migration file must start with:
    • -- Tables affected: ...
    • -- recovery.not-started: ...
    • -- recovery.partial: ...
    • -- recovery.completed: ...
    • one or more -- verify: <description> | <SQL> checks
  • Verify queries must return rows only when something is wrong.
  • Verify queries must exclude NULL and empty values when those would create false positives.
  • A migration is recorded as applied only after all verify checks pass.

Pre-Production Validation Rules

  • Test pending migrations on a dump of the current production DB, not on fixtures.
  • Use a local MariaDB Docker container matching the production version and collation.
  • Execute each migration file as one DB session so session variables such as SET FOREIGN_KEY_CHECKS = 0 remain in effect for the whole file.
  • If migrations fail in Docker, fix them before touching production.

Common Pitfalls

  • Do not use tools that naively split SQL on bare ;. String literals may contain semicolons.
  • SET FOREIGN_KEY_CHECKS = 0 is session-scoped. If the file is split across multiple sessions, FK checks come back on.
  • When adding a new FK to legacy data, repair missing parent rows before enforcing the constraint unless data loss is explicitly acceptable.