Skip to content
About
Main content

Propagation through a derived DAG

How does one change spread through derived tables, and how do you find out who breaks?

Single-hop compatibility (one , one ) is a solved problem. The unsolved case is one source change flowing into N derived models, each of whose schema is itself inferred from its query.

SELECT * turns a safe change into a schema change at every hop

SELECT * copies structure automatically. So an upstream additive change, which is the safest operation in the whole algebra, becomes a schema change at every downstream step, with no code change and no review.

, but at the price of a manual edit per hop for every addition. There is no third option. The trade is automatic versus manual toil, and both fail at scale, in different directions.

-- Propagates every upstream change (automatic drift):
CREATE VIEW shipments_clean AS SELECT * FROM shipments;

-- Stops propagation (manual toil per added column):
CREATE VIEW shipments_clean AS
SELECT shipment_id, status, weight_kg, price_cents, occurred_at FROM shipments;

Lineage and usage

Column-level lineage turns "what breaks if I drop this ?" from a meeting into a graph query. That is the real argument for as infrastructure rather than documentation.

But the is the transitive closure from the changed column (everything reachable from it), intersected with actual usage. Without usage telemetry, the closure is a wild over-estimate: everything looks breaking, and you get change paralysis.

That is the less visible failure mode, and often the more expensive one. A table nobody dares to modify is not a stable interface. It is a frozen one.

SELECT * turns a safe upstream addition into a schema change at every hop. Column lineage plus usage telemetry is what makes "who breaks?" answerable.