The config is the blast radius
A session-wide switch can silently widen every Delta table touched by later writes.
Scope the opt-in →Schema enforcement is loud by default. One shared session setting can turn that safety rail into silent, table-wide schema drift.
Opt in per write on bronze and landing. For silver and gold, add columns only through a reviewed schema change.
A session-wide switch can silently widen every Delta table touched by later writes.
Scope the opt-in →An idempotent SQL demo shows the schema before, the trap, the new column, and the safe reset.
Run the demo →Evolution needs both an opt-in and a real source-schema column covered by the MERGE action.
Read the two gates →Last verified against official Databricks docs: October 14, 2026. Corrections: none yet — first published as Issue 026, October 14, 2026.
For a Delta INSERT, an unknown target column fails with UNRESOLVED_COLUMN.WITH_SUGGESTION (SQLSTATE 42703). For MERGE with INSERT * or UPDATE SET *, enforcement requires every target column in the source but silently ignores source columns that do not exist in the target.
SET spark.databricks.delta.schema.autoMerge.enabled = true;.option("mergeSchema", "true") on the individual bronze or landing write.WITH SCHEMA EVOLUTION or the table API .withSchemaEvolution(). These forms require Databricks Runtime 15.4 LTS or above.COPY_OPTIONS ('mergeSchema' = 'true') for that load.New columns append at the end of the target schema, their case is preserved, and old rows read NULL for those columns. For MERGE, the new column must exist in the source schema and either be named in an assignment or be covered by UPDATE SET * / INSERT *. A computed expression can populate a source column, but assigning an expression to a new target name does not itself define that missing source-schema column.
Automatic evolution is for additive columns. Renames, type changes, and drops require explicit operations such as overwriteSchema or Delta column mapping, depending on the change. For silver and gold, keep evolution reviewed and explicit: ALTER TABLE … ADD COLUMN.
SET spark.databricks.delta.schema.autoMerge.enabled = false;
CREATE OR REPLACE TABLE demo_evolve (
id INT,
amount BIGINT
);
INSERT INTO demo_evolve VALUES (1, 100);
CREATE OR REPLACE TEMP VIEW demo_source AS
SELECT 2 AS id, 200 AS amount, 'vip' AS tier;
DESCRIBE demo_evolve;(id, int) and (amount, bigint).SET spark.databricks.delta.schema.autoMerge.enabled = true;
INSERT INTO demo_evolve
SELECT * FROM demo_source;
DESCRIBE demo_evolve;
SELECT *
FROM demo_evolve
ORDER BY id;DESCRIBE returns 3 schema rows: (id, int), (amount, bigint), (tier, string). The ordered data result returns 2 rows: (1, 100, NULL) and (2, 200, 'vip').SELECT * write carries an extra column into production. The column appends at the end, positional extracts shift, streaming readers terminate, and concurrent writers can conflict. This describes documented mechanics applied to a hypothetical sequence; it is not an observed event or measured result.The first two statements intentionally reset the demo table to its original two-column state. Without that reset, tier would already exist and the final INSERT would succeed.
SET spark.databricks.delta.schema.autoMerge.enabled = false;
CREATE OR REPLACE TABLE demo_evolve (
id INT,
amount BIGINT
);
INSERT INTO demo_evolve VALUES (1, 100);
INSERT INTO demo_evolve (id, tier)
VALUES (3, 'x');
UNRESOLVED_COLUMN.WITH_SUGGESTION (SQLSTATE 42703) because tier is not a target column.ALTER TABLE demo_evolve ADD COLUMN tier STRING;
INSERT INTO demo_evolve (id, tier)
VALUES (3, 'x');The reviewed DDL changes the contract before the data arrives. The insert is then valid, and the schema change remains visible in table history.
ALTER TABLE only.Gate 1 — opt in: use the session configuration, SQL WITH SCHEMA EVOLUTION, or .withSchemaEvolution(). Without an evolution opt-in, enforcement keeps the target schema unchanged and INSERT * / UPDATE SET * ignores extra source columns.
Gate 2 — prove the source column: the column must exist in the source schema and be named in an assignment or covered by INSERT * / UPDATE SET *. Assigning a computed expression to a new target name does not define a missing source-schema column.
Practitioner pattern — not official documentation: diff target-table columns against source columns before the merge, then apply the delta through an explicit reviewed ALTER TABLE. This pattern is discussed in the Databricks Community thread “MergeSchema Not Working,” verified loading October 14, 2026.
Version floor: SQL WITH SCHEMA EVOLUTION and the table API .withSchemaEvolution() require Databricks Runtime 15.4 LTS or above.
CREATE OR REPLACE TEMP VIEW schema_before AS
SELECT column_name,
full_data_type,
ordinal_position
FROM my_catalog.information_schema.columns
WHERE table_schema = current_schema()
AND table_name = 'demo_evolve';
my_catalog with the table's catalog. CREATE VIEW … AS DESCRIBE is not valid Spark SQL, so snapshot information_schema.columns instead.SELECT column_name,
full_data_type,
ordinal_position
FROM my_catalog.information_schema.columns
WHERE table_schema = current_schema()
AND table_name = 'demo_evolve'
EXCEPT
SELECT column_name,
full_data_type,
ordinal_position
FROM schema_before;
ALTER TABLE.DESCRIBE HISTORY is the audit trail for the table change.NULL, and a schema change terminates streams reading the table.Build the Databricks and data-engineering skills behind deliberate Delta Lake schema management.
Practice Databricks workflows that make schema evolution explicit and reviewable.
Browse DataCamp Databricks courses →Strengthen pipeline contracts, validation, and production change control.
Browse DataCamp data engineering courses →Partner links: we may earn a commission if you enroll through these recommendations, at no extra cost to you.
WITH SCHEMA EVOLUTION and .withSchemaEvolution() require Databricks Runtime 15.4 LTS or above.INSERT, batch or streaming writes, MERGE, and COPY INTO, with the operation-specific opt-ins described above.