When is committing Django's makemigrations --merge output unsafe, and what does the merge migration not do for you?
answer
- the prompt's warning
- an empty operations list
- same field on both branches
- order differs per database
basics
~20 sA merge migration only joins the graph; it reconciles nothing. If both branches change the same field or model, databases can apply them in different orders and end with different schemas, so edit the migrations or add an explicit fix-up instead.
solid answer
~40 sThe merge file has an empty `operations` list and two `dependencies`, so it guarantees only that both branches run before anything that comes later. It does not compare their operations or pick a winner. Django's own prompt says merging works only if the operations "do not conflict with each other (working on different fields or models)". If branch A sets `Order.note` to `max_length=200` and branch B to `100`, staging, which already applied A, applies B last, while another database may apply them in the other order, so the column ends differently. After any merge I run `makemigrations --check`: if the merged migration state disagrees with `models.py`, the autodetector wants a new migration. For real conflicts I rebase one branch's migration or add a migration stating the intended final definition.
go deeper
Remember that the merge migration has no operations and only makes both branches run before it.
Explain why overlapping operations make the result depend on application order, and use makemigrations --check to confirm state matches models.py after a merge.
Recognise the unsafe patterns in review and choose between rebasing an unshared migration and adding an explicit convergence migration.
Weigh how much parallel schema work the team allows on the same models, and how reviews catch overlapping migrations before they merge.
## What the merge migration is `python manage.py makemigrations --merge` resolves a forked migration graph (two **leaf nodes** in one app) by writing a new migration whose `operations` list is empty and whose `dependencies` name both leaves. That is all it does. It does not: - compare the two branches' operations for overlap; - reorder, rewrite or deduplicate any operation; - check that the final schema matches `models.py`; - fix data assumptions, such as a `RunPython` on one branch that expects the other branch's column. The interactive prompt says so directly: merging "will only work if the operations printed above do not conflict with each other (working on different fields or models)". With `--noinput` there is no prompt at all, and the merge is written regardless. ## Why order matters once branches overlap Within one app the graph now says "both branches before `0043_merge`", but nothing orders branch A relative to branch B. Different databases reach the merge by different paths: | Database | History | Order of the two 0042 migrations | |---|---|---| | Staging | A was deployed first, B arrived later | A, then B | | A developer's laptop | B was checked out first | B, then A | | Fresh CI database | applies the whole graph at once | whatever order the graph walk picks | When A and B touch different things, such as two new fields, every order gives the same schema. When they touch the **same** field, model, index or constraint, the last one applied wins, and the databases diverge silently. ## Unsafe patterns 1. **Both branches alter the same field**: different `max_length`, `null`, `default` or type. 2. **One branch renames or removes what the other alters**: `RenameField` on A, `AlterField` on the old name on B, which fails outright on one of the orders. 3. **Same constraint or index name** created with different definitions. 4. **Data dependency**: B's `RunPython` backfills using a column that only A adds. 5. **Both branches add a field with the same name** but different definitions: the second `AddField` fails. ## What to do instead - **Read the operations** the merge command prints before confirming. - If the branches conflict and one migration has not yet run anywhere shared, **rebase it**: delete it, pull the other branch, and regenerate with `makemigrations`, so it depends on the other 0042 and states the final definition. - If both already ran in shared databases, keep the merge but **add a follow-up migration** that sets the intended definition explicitly, so every database converges regardless of the order it took. - After any merge, run `makemigrations --check` (or `makemigrations --dry-run`). The autodetector compares the migration **project state** with `models.py`; any difference means the merged history does not describe your models and a new migration is needed. ## Why interviewers ask Many candidates know the command. The follow-up separates them: can they explain that a merge migration is graph plumbing, not conflict resolution, and name the case where it leaves two databases with different schemas?
- Both branches added a field with the same name but different types; what happens on a fresh database after a plain merge?Whichever `AddField` runs second fails, because the column already exists, or the migration state reports a duplicate field. The merge file cannot prevent it; one branch's migration has to be rewritten so only one definition of that field exists in history.
- Does makemigrations --merge with --noinput still check the operations?It still computes and, at normal verbosity, prints each branch's operations, but it does not stop to ask. The non-interactive questioner answers yes to the merge, so in scripts nothing prevents a conflicting merge from being written.
A merge migration is like two road crews both reporting 'done' before a street reopens: the city only checks that both crews finished, not whether they paved the same lane twice with different materials.
saying these in an interview costs you the question
- The merge migration reconciles conflicting AlterField operations
- makemigrations --merge refuses when both branches touch the same field
- Every database applies the two branches in the same order
- If migrate succeeds after a merge, all schemas must be identical
- --noinput makes the merge skip unsafe branches