skip to content

Migrations & Drift

How the mapping and the deployed schema are kept in step: generated from the mapping or owned by scripts, run at deploy, backfilled, tested. Interviewers probe it because drift surfaces as an outage.

on this pageshow

questions

18

When a data-access layer builds the schema from its mapping, what do its re-create, auto-alter, validate-only and none modes do?

level: juniorimportance: must knowfreq 60%

answer

  1. four levels of intrusiveness
  2. two modes write, two only look
  3. drop-and-rebuild versus add-in-place
  4. validate compares, then refuses to boot
  5. deployed schema belongs to scripts

basics

~20 s

Schema-from-mapping modes differ in how far the layer will change the database: re-create drops and rebuilds the mapped tables, auto-alter adds what is missing in place, validate-only compares and refuses to start on a mismatch, and none touches nothing.

solid answer

~40 s

A mapping already describes tables, columns, types and keys, so most data-access layers can emit the `CREATE TABLE` statements for it. The modes differ by how destructive they are. **Re-create** drops the mapped objects and builds them again at every startup, so all their rows are lost — it suits a disposable local or test database. **Auto-alter** compares mapping to schema and applies the missing pieces in place; it is typically additive, adding tables, columns and indexes rather than removing or narrowing them. **Validate-only** performs the same comparison, changes nothing, and aborts startup when the two disagree. **None** does no comparison and no change. In deployed environments the schema is normally owned by reviewed migration scripts, and the layer runs in validate-only or none.

go deeper

for a junior

Learn the four names and what each one does to the data: rebuild, add in place, only check, do nothing. Knowing which of them destroys rows is the point of the question.

for a middle

Explain why an additive mode still accumulates drift: it adds what is missing but does not remove what the mapping abandoned, so the two shapes diverge in the direction the tool cannot repair.

for a senior

Say where each mode sits in your pipeline and why the deployed schema is owned by reviewed scripts, with the layer reduced to a comparison that fails startup instead of a generator that writes.

for a principal

Frame it as a control question: generation moves schema authority into whichever process starts first, with no review, no record and no ordering. That is the tradeoff to argue, not the convenience.

## What "schema from the mapping" means A mapping already carries almost everything a `CREATE TABLE` statement needs: which class maps to which table, which field to which column, the declared types and lengths, which field is the key, which associations imply a foreign key, and which fields must be unique. Because that information is machine-readable, most data-access layers can turn it around and **emit the schema instead of reading it**. The question every project has to answer is not whether the layer *can* do that, but **how much of the deployed database it is allowed to touch**, and that is what the modes express. Treat them as a ladder of intrusiveness rather than four unrelated options. ## The four modes | Mode | What it does at startup | Effect on stored rows | Where it belongs | |---|---|---|---| | **Re-create** | Drops the mapped objects and creates them again from the mapping | Everything in those tables is gone | A disposable local database, or a fresh database per test run | | **Auto-alter** | Compares, then applies missing pieces in place — typically additive | Rows survive; new columns arrive empty or defaulted | Local experimentation only; not a deployed environment | | **Validate-only** | Compares and reports; changes nothing; aborts startup on a mismatch | None | Deployed environments, as a drift gate | | **None** | No comparison and no change | None | Deployed environments where something else does the checking | Two of these write to the database and two do not, and that is the line that matters. Re-create and auto-alter are **generators**; validate-only and none are **passive**. ## Why the additive mode is not the safe middle ground Auto-alter looks like a compromise — it keeps your data and quietly fixes what is missing — and that impression is why it causes incidents. - **Nothing was reviewed.** The statements are computed at process start and executed immediately. No one read them, and nothing about the change was agreed before it ran. - **It only moves in one direction.** Because it typically adds rather than removes or narrows, everything the mapping stopped using stays behind: dead columns, stale indexes, constraints nobody wants. Drift accumulates precisely where the mode cannot clean it up. - **Names are invented.** Constraints and indexes it creates get generated names, so the deployed database ends up with objects nobody can refer to reliably later. - **Timing is whatever the process does.** The change runs when the application happens to start, with no consideration of table size or of traffic already hitting that table. - **Several instances may try at once.** When a deployment starts many copies of a service together, each one independently decides the schema needs altering. - **No record survives.** Afterwards there is no artifact saying what ran, so two environments can differ with nothing to compare. Re-create is more obviously dangerous and therefore, oddly, safer: nobody mistakes "drops every table" for something to leave enabled. ## Validate-only and none Validate-only is the mode most deployed services want. It performs the same structural comparison the generator would, then **refuses to start** rather than repairing anything. Its value is that a schema problem becomes one loud failure at startup instead of a confusing error the first time some rarely used query runs. Its limit is equally important: it checks **structure the mapping knows about**, not whether the stored data suits the new shape, and typically it does not object to extra columns the mapping never mentions. None is the honest choice when a separate gate already covers the same ground, or when the mapping deliberately describes only part of a larger shared database. ## The usual environment gradient 1. **Laptop / unit tests** — re-create against a throwaway database, because rebuilding from the mapping is faster than maintaining fixtures. 2. **Shared development or integration** — scripts, so the environment behaves like the real one. 3. **Staging and production** — reviewed migration scripts own the schema outright; the layer runs validate-only or none. ## What none of the modes do No mode moves data. Generation emits structure — tables, columns, keys, indexes — and never the statements that populate a new column or reshape existing values; designing that work is a separate concern. A generated schema also cannot express what the mapping does not model, such as storage options, partitioning or specialised index shapes. And a passing validation is not a guarantee that the rows in those tables are meaningful — only that the structure the mapping requires exists.

  • Why is re-create acceptable for tests but auto-alter is not acceptable anywhere deployed?
    Re-create is used against a database whose contents are worthless and rebuilt on purpose, so its destructiveness is the feature. Auto-alter is used against a database whose contents matter, applies statements nobody reviewed at whatever moment a process starts, and leaves no artifact describing what it did.
  • If the schema is owned by scripts, is there still a reason to keep the generator around?
    Yes, as an authoring aid and a check. The generator can produce a first draft of a change for a human to review and edit, and the same comparison logic, run in validate-only mode, tells you whether the mapping and the schema still agree.

saying these in an interview costs you the question

  • Says auto-alter is fine in production because it never drops anything
  • Thinks re-create preserves the rows already stored
  • Believes validate-only repairs the mismatch it reports
  • Expects the additive mode to remove columns the mapping no longer maps
  • Treats a passing structural check as proof the stored data fits
open as a page

Why do teams replay the whole ordered chain of schema change units from an empty database in tests?

level: juniorimportance: must knowfreq 62%

basics

~20 s

Replaying every change unit from empty proves the chain itself still builds a valid schema, catching ordering and dependency errors that a developer's long-lived database hides. Running only the newest unit leaves the rest of the chain untested.

open as a page

How do you write a backfill or seed insert so that re-running it after an interruption is harmless and resumes?

level: middleimportance: must knowfreq 60%

basics

~20 s

Write the predicate so it selects only outstanding work, compute values absolutely from source columns rather than adding to the current one, guard inserts on a unique key, and commit progress so a restart continues where it stopped.

open as a page

When data must be moved alongside a schema change, what does writing it as SQL statements buy over running it through the mapping?

level: middleimportance: must knowfreq 64%

basics

~20 s

SQL statements run set-based against the schema as that point in history left it; no later code edit can alter them. Running the change through the mapping adds logic the database lacks, but ties a historical step to moving code.

open as a page

Why must a migration script generated by diffing the mapping against the last known schema be reviewed before it runs?

level: middleimportance: must knowfreq 55%

basics

~20 s

A diff compares two end states, not the edit that produced them, so it cannot see intent. A renamed field reaches the script as a dropped column plus an added one, which destroys the data unless a human rewrites it.

open as a page

When a service applies schema changes during its own boot, what must happen in what order before it serves traffic?

level: middleimportance: must knowfreq 60%

basics

~20 s

Boot applies the pending schema changes first, then checks the mapping against the schema that now exists, and only then reports the instance ready. A failure at either step should stop the boot instead of admitting traffic.

open as a page

After CI applies the whole change chain to a throwaway database, what else must the job assert?

level: middleimportance: must knowfreq 56%

basics

~20 s

That the schema the chain produced is the schema the code needs: compare it against the object-to-table mapping and fail on missing tables, columns, types or nullability, then run the data-access tests against that same database.

open as a page

Several instances of a service boot together and each runs the schema-change step; what should the instances that do not apply it do?

level: seniorimportance: must knowfreq 52%

basics

~20 s

They should block on the same exclusive lock, wait for the winner to finish, then re-read what is now applied and continue their own boot. Exiting, restarting, or skipping ahead to readiness are all worse than waiting.

open as a page

In a versioned schema change set, what are reference or lookup seed rows, and why are they shipped with the schema?

level: juniorimportance: should knowfreq 52%

basics

~20 s

Reference rows are fixed lookup values the code and the constraints depend on: statuses, roles, currencies, category codes. Shipping them as guarded inserts in the ordered change set makes every database start identical and reviewable, instead of hand-inserted per environment.

open as a page

How does a failed schema change surface differently when the runner is embedded in application boot versus run as a separate deploy step?

level: middleimportance: should knowfreq 55%

basics

~20 s

An embedded runner turns a bad change into unready, restarting instances and a rollout that stalls until it times out. A separate one-shot step fails once, before any instance starts, as one red stage with one owner.

open as a page

Why does a data-migration step written in application code against mapped classes break months later, and how is that prevented?

level: seniorimportance: should knowfreq 56%

basics

~20 s

A data step must reproduce a shape frozen in history, but the classes it imports keep changing: it either stops building or silently writes different columns. Pin it to a copied shape, or write plain SQL over explicit columns.

open as a page

Your service compares its mapping with the live schema at boot and refuses to start on a mismatch: what does that gate catch, what does it miss, and what does it cost?

level: seniorimportance: should knowfreq 48%

basics

~20 s

A boot-time schema check catches structural drift the mapping names — a missing table or column — turning it into one loud startup failure rather than a query-time error. It misses data, semantics and unmapped objects, and costs a crash-looping instance.

open as a page

A boot-time schema change stalls behind a lock and the rollout hangs; which waits do you bound, and how?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Bound two waits: the runner's wait for its exclusive lock, and each statement's wait for the object locks it needs. Both must expire inside the platform's startup deadline, so the runner fails with a message rather than being killed mid-change.

open as a page

How would you prove a schema change's recovery path works before an incident forces you to use it?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Execute it in a test rather than assuming it: apply the change, write rows through the new shape, run the undo or corrective unit, then assert both the schema and the surviving data. A recovery step that has never run is untested code.

open as a page

A change applies in milliseconds in tests but stalls the deploy on a production-sized table. How would you catch that beforehand?

level: seniorimportance: should knowfreq 52%

basics

~20 s

Rehearse the pending change against a restored copy at realistic row counts, under a small concurrent workload, and record elapsed time and lock hold time per unit against an explicit budget. Empty tables measure acceptance, never cost.

open as a page

When should data movement be split out of the schema change set rather than shipped inside it, and what does that split cost?

level: principalimportance: should knowfreq 45%

basics

~20 s

Split data movement out when the run would hold the release open and the new code is correct against incomplete data. The cost: the change history no longer reproduces the data, and an unfinished run tends to become permanent.

open as a page

When one team runs both mapping-first generation and hand-written scripts against the same database, how do you decide who owns the schema?

level: principalimportance: should knowfreq 40%

basics

~20 s

Pick one artifact as the source of truth — the mapping or the scripted schema — and make the other derived and continuously checked against it. Either direction works; two independent writers with no arbiter is what produces drift nobody can resolve.

open as a page

Across a fleet of services, how would you decide where the schema-change runner runs, and what may it assume about which versions are serving?

level: principalimportance: nice to knowfreq 36%

basics

~20 s

Decide by how instances start and who should own the failure: autoscaling and unattended restarts argue for a one-shot step outside boot. The runner may never assume its own version is live; the previous one is still serving.

open as a page