3.6 KiB
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. Usegorm:"-: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
.sqlmigration 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 = 0remain 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 = 0is 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.