skip to content

When should a batch load auto-evolve the target table's DDL instead of failing loudly?

level: seniorimportance: must knowfreq 62%

answer

  1. ask if you could undo it
  2. ask who reads this table
  3. additive and widening are the safe classes
  4. dropping and narrowing never go automatic
  5. apply it and still tell somebody

basics

~20 s

Auto-evolve additive, reversible changes in a raw landing zone — a new nullable column, a widened type — and always notify. Fail loudly on destructive or ambiguous ones: drops, renames, narrowing retypes, and anything in a published table consumers depend on.

solid answer

~50 s

Split the decision on two axes: **is the change reversible**, and **what is downstream of this table**. In a raw landing zone with no consumers reading it directly, adding a nullable column or widening a type costs nothing and can be applied automatically — you can always ignore a column, and no existing query breaks. In a published table that dashboards and models read, even an additive change deserves a human, because column semantics and lineage are now someone else's contract. Destructive changes — dropping a column, narrowing a type, a rename — should never be automatic in either zone, because they are not recoverable from the target alone and a rename is indistinguishable from a drop-plus-add. The practical pattern is **evolve and alert**, not evolve or alert: apply the safe change, record it in a schema-change log, and page a human for the rest.

code

yaml · 10 lines
yaml
drift_policy:
  add_nullable_column:   { action: evolve, notify: true }
  widen_type:            { action: evolve, notify: true }
  narrow_type:           { action: halt }
  drop_column:           { action: halt }
  rename_suspected:      { action: halt }
  max_auto_added_columns_per_run: 3
zones:
  landing:   { policy: above }
  published: { policy: halt_on_any_change }

go deeper

for a junior

Know that some source changes can be applied to the target automatically and others must stop the load, and that adding a nullable column is the safe end of that range.

for a middle

Explain the classification — additive, widening, narrowing, drop, rename — and why widening preserves data while narrowing does not. Be able to say why a rename cannot be automated.

for a senior

Demonstrate the two-axis decision in a real pipeline: reversibility and blast radius, different policies for landing versus published tables, evolve-and-notify rather than evolve-silently, and a rebuildable raw layer behind it.

for a principal

Own the policy across sources: who is accountable for a schema-change notification, which tables are contract-bound and therefore frozen to automation, and what circuit breakers stop a misbehaving producer from reshaping the warehouse overnight.

## The decision, stated plainly Every schema change that arrives at your loader takes one of two branches: the pipeline applies it and keeps running, or the pipeline stops and a human decides. Getting this split right is what separates an ingestion platform that people trust from one that either wakes someone up nightly or corrupts things quietly. The two questions that decide the branch are **reversibility** and **blast radius**. ## Axis one: is the change reversible? **Reversible (candidate for automation).** Adding a nullable column: nothing existing changes, and if the column turns out to be junk you drop it later with no data loss. Widening a type — integer to bigint, numeric to a wider precision, a typed column to string in a raw zone: every existing value survives the widening, so the operation is safe to apply and the information content only grows. **Not reversible (must halt).** Dropping a column destroys data unless you have the source to reload from. Narrowing a type — string back to integer, timestamp to date — fails on the first non-conforming value or truncates silently. A rename is worse than either, because your tooling cannot tell it apart from a drop plus an add and will therefore guess, and a wrong guess either orphans history or grafts one column's history onto a different column's meaning. A useful rule: **never auto-drop and never auto-narrow.** If your evolution logic has a code path that removes a column or reduces a type's range, that path is a future incident. ## Axis two: what is downstream? A raw landing table that only the transformation layer reads has almost no blast radius. Adding a column there affects nothing until someone chooses to project it. A published mart that fifty dashboards and a reverse-ETL sync read is a different object. Auto-evolving it means a column appeared in a shared asset with no review of its name, its semantics, its nullability or its ownership. Even when the mechanical change is safe, the governance is not, and the column will be permanent within a week. This is why the mature architecture is two-zone: **permissive at landing, strict at publish**. Automation lives in the landing zone; the publish boundary is an explicit projection that a human changes deliberately. ## Evolve *and* alert The framing "auto-evolve or fail loudly" is a false binary. Applying a safe change and telling nobody is how a schema accumulates twelve columns nobody can explain. The working pattern is: 1. Detect the difference between the observed source shape and the recorded expected shape. 2. Classify it: additive / widening / destructive / ambiguous. 3. Apply the safe classes, record the change with a timestamp and the run that caused it, and emit a notification to a channel the data team reads. 4. Halt the load for the unsafe classes, with a message naming the exact column and the exact difference, and route the batch somewhere it can be inspected rather than half-loaded. Step 3's notification is what makes automation acceptable — the change is applied but not invisible. ## What makes automation go wrong **Type ping-pong.** A source column that alternates between integer-looking and string-looking values across batches will, under naive widening logic, cause repeated ALTERs. Widen once, in one direction, and never widen back. **Column explosion.** A buggy producer emitting keys derived from data — a per-tenant or per-day field name — will grow your table without bound. Cap the number of columns an automatic evolution may add in a single run and halt above the threshold; that ceiling is a cheap circuit breaker. **Silent semantic change.** The riskiest change is invisible to any structural check: the column keeps its name and its type, and its *meaning* changes — `amount` moves from gross to net, `status` gains a new enum value that downstream filters do not handle. No DDL evolution logic will catch this, which is why value-level quality checks sit alongside schema checks rather than being replaced by them. **Evolving a table under a running consumer.** Adding a column to a table an external tool introspects can force a refresh, break a `SELECT *`-based view, or change a downstream model's output shape. Know which of your targets have that property. ## Making automatic changes recoverable Automation is only defensible if you can undo it. Two things make that true: an **immutable raw layer** you can rebuild the target from, and a **schema-change log** that records what was applied when. With both, an evolution that turns out to be wrong is a rebuild, not an archaeology exercise. Without them, "the pipeline changed it" is where the investigation ends. ## How to answer this in an interview Do not answer with a policy; answer with a decision procedure. Name the classification, name the two axes, state the never-rules (never auto-drop, never auto-narrow, never auto-evolve a published contract), and describe the notification that makes the automated branch safe. Then say what you keep so you can roll it back.

  • What would make you refuse to auto-evolve even a plainly additive change?
    If the table is a published asset with named consumers, or if the new column carries sensitive data that needs a classification and access decision before it exists anywhere. Also if the source has a history of emitting data-derived field names — one unbounded producer can add hundreds of columns in a night, and a per-run cap should halt that rather than absorb it.
  • How do you make an automatically applied schema change reversible?
    Keep the raw landed data immutable and separate from the evolved target, and write every applied change to a schema-change log with the run that caused it. Then undoing is rebuilding the target from raw under the corrected definition. Without an immutable raw layer, an automatic ALTER is a one-way door.
  • Which drift class does no DDL-evolution logic catch at all?
    A semantic change with a stable structure: the same column name and type, but the meaning shifted — gross to net, a new enum value, a unit change from cents to dollars. Structural comparison sees nothing. Only value-level checks on distributions, ranges and distinct values will notice, which is why they belong beside the schema check, not instead of it.
  • Why is 'never auto-narrow' a firmer rule than 'never auto-widen'?
    Widening preserves every existing value; narrowing destroys the ones that no longer fit, either by failing on them or by truncating. And once a column has been narrowed in the target, the discarded precision is only recoverable if the raw layer still holds it. The asymmetry is about information loss, not about difficulty.

saying these in an interview costs you the question

  • Auto-evolve everything so the pipeline never fails
  • Auto-drops target columns when the source stops sending them
  • Treats a rename as safely detectable and applies it automatically
  • Applies safe changes silently with no log or notification
  • Uses the same evolution policy for landing and published tables

context