A rule we landed on early, and the reasoning isn’t the obvious one.
Once a migration is on main, we never edit it. Not the initial schema migration, not the one you wrote yesterday. New change → new file. Bad change → revert in a new file.
The obvious reasoning is “production.” If a migration has run in production, editing it is a bug — you’d be lying to every database that’s already applied it. Fair, but irrelevant for us right now. Bugspot is in beta and the production database has been reset more than once.
The actual reasoning is everyone else’s local dev DB.
When you edit 001_initial_schema.sql in place, every developer who pulls your branch has their local Postgres reach for a checksum it can’t satisfy. Goose refuses to run. Their options are (a) figure out which migration drifted, (b) make db-reset and lose all their seeded data, or (c) Slack you. Most pick (c). You’ve just turned a one-person change into a two-person tax.
Treating migrations as append-only — even when the schema “isn’t real yet” — turns this into a non-issue. Your branch adds 002_something.sql. Their make db-migrate runs it. No drift, no reset, no pings.
This is one of the few places where “we’re in early development, breaking changes are fine” is not the right framing. Code can be reshaped freely. Schema migrations can’t, because they’re a contract with every dev workstation, not just with the production database.
Code-level: rename, refactor, delete with abandon. Migrations: append-only. The asymmetry is on purpose, and it’s worth more than the small irritation of writing one more file.