Why two branches break the history
Migration tools keep an ordered history of schema changes and a record in the database of which ones have run. When two branches each add a migration on top of the same parent, the history forks. Merging the code is easy; merging the history is not, because the tool needs one ordered chain, or an explicit statement that the two changes are independent. Until you give it one, it will either refuse to run, ambiguously choose, or let two developers' databases drift apart.
The danger is not the error message, which is helpful. It is the quick fix: editing a migration that has already run somewhere, renaming it, or faking its application so the error goes away. Those leave databases in different states under the same history.
- Find which migrations have run in each environment before changing any file.
- Do not delete or rename a migration that any environment has applied.
What each tool does
Django: its documentation says that when two new migrations for the same app are not ordered relative to each other, Django notices and may offer to linearise them if that looks safe, otherwise you edit the dependencies yourself. The consequence to check is that a database which applied one branch's migration must still reach a consistent state when the other runs. Alembic: two revisions sharing a parent make multiple heads, and upgrade head becomes ambiguous. alembic merge creates a merge revision whose down_revision is a tuple of both parents; it can be empty, or reconcile the two. Rails: migrations are recorded by version in the schema_migrations table, and the guide advises that a merge conflict in the schema file is resolved by running db:migrate to regenerate it, not by editing it by hand.
In each case the merge changes the history, not the data, so it should be rehearsed on a copy that has applied each branch in turn.
- Django: read the two dependency chains and decide the order.
- Alembic: use the merge command, then run upgrade on a copy at each parent.
- Rails: regenerate the schema file by migrating, then diff it.
Check that the two changes are really independent
A merge that is mechanically valid can still be wrong. If both branches alter the same table, rename the same column or add constraints that depend on each other, running them in either order may produce different schemas or fail on real data. Test both orders on a copy with realistic data. Compare the resulting schemas and run the application's tests against each.
Reversibility matters too. Django states that a data step without a reverse callable cannot be migrated backwards, and Rails raises an error for an irreversible migration. Know which of the two migrations can be undone before you decide the order, because that order decides how you back out a bad deploy. On MySQL, where Django does not wrap a migration in a transaction and a failure part-way must be unpicked by hand, keep a restore point you have tested.
- Apply the two branches in both orders on a copy.
- Diff the final schemas and run the test suite on each.
Where the paid job fits
The add-column outcome is built around one new column on one table, rehearsed on a staging copy under write load, with its rollback actually run. It does not include reconciling migration history: if your change sits behind a migration conflict, settle the history first, using the steps above, and rehearse the change on a clean chain. It covers Django, Rails and Alembic or plain SQL, one table and one column.
It does not rewrite your migration history in production, edit migrations that have run there or repair a database that has already diverged from its history, and no fixed-price job here covers those. They need a diagnosis of the actual state first. For several changes in sequence, the data-layer project can list them as items, each with its own rehearsal and undo step.
- Keep a note of each environment's migration state in the handover.
- Agree a freeze on new migrations while a merge is in progress.
Sources and limits
- Django 5.2: Migrations Checked 2026-10-11.
- When two developers commit migrations to the same app, Django detects two new migrations that are not ordered relative to each other and may offer to linearise them or require editing dependencies by hand.
- Migrating backwards uses a target migration; a RunPython step without a reverse callable cannot be reversed, and MySQL has no transaction support around schema alterations, so a failed migration must be unpicked by hand.
- Alembic: Working with branches Checked 2026-10-11.
- When two revisions share a parent there are multiple heads and upgrade head is ambiguous; alembic merge creates a merge revision whose down_revision is a tuple of both parents, and upgrade heads can apply every head.
- Rails 8.1 guide: Active Record Migrations Checked 2026-10-11.
- Rails tracks run migrations in the schema_migrations table; db:migrate:status reports files that no longer exist; editing a migration already run in production is discouraged; merge conflicts in the schema file are resolved by running db:migrate.