intermediate 2 min answer Multiple choice

A pipeline lands 40 SaaS objects into bronze as typed tables - one column per source field. A vendor adds three fields and renames one. The next load silently drops the new fields and fails on the rename, and the vendor's API only serves 30 days of history. Which bronze design holds up?

bronzeschema evolutionjsonreplaylakehouse
Pick one
Show the full answer Hide the answer

The deciding property

Whether bronze can answer a question you have not asked yet. The vendor's 30-day API window is the fact that settles this: anything bronze discards at load time is gone for good in a month. A field dropped because the table had no column for it is unrecoverable, and nobody notices until a new report needs it.

Why the payload column wins

Land the record as it arrived - one payload column of JSON or a variant type - with ingest metadata beside it: extraction timestamp, source system, connector version, batch id, and the primary key promoted to a real column so the table can be partitioned and merged.

Three consequences:

  • Unknown fields are kept at zero design cost. The three new fields are in the payload the day they appear, and the silver model that needs them is a one-line change with full history behind it.
  • A parsing bug becomes replayable. Fixing a cast or a renamed field is a rebuild of silver from bronze, not a re-extraction from an API that no longer has the data.
  • The load stops failing on source changes, which is what stops the 02:00 page that nobody can act on until the vendor answers.

The cost is real: an uncompressed JSON payload typically stores 2 to 4 times the bytes of the same data in typed columns, and queries against bronze pay parse cost per row. Both are acceptable because bronze is written often and read rarely - by the silver build and by an engineer investigating. Promote the few columns that are actually filtered on.

Why the other options fail

  • "Add columns automatically." Attractive and it handles the additive case. It does not handle the rename, the type change from integer to string, or a nested object becoming an array - and an auto-widening table drifts to 300 columns nobody can explain. It also makes the schema a function of whichever extract ran first.
  • "Reject unknown fields." Safe in the sense that nothing is silently wrong, and it converts every vendor release into an outage. Right for a regulated interface with a negotiated contract; wrong for 40 SaaS APIs that change without telling you.
  • "Infer the schema per extract and replace the definition." The table's columns then change under consumers with no version history, and one bad extract rewrites the definition for everything downstream.
  • "Move typing into bronze." This is the design that produced the problem. Typing at landing time is where fields get dropped, and it puts the decision at the moment you know least.

When this is the wrong answer

For a stable high-volume internal source - a database you control through change data capture, a fixed-schema clickstream of billions of rows a day - typed columns at landing time are cheaper on every axis and the schema changes only when you change it. Use a payload column where the schema is someone else's decision, and typed columns where it is yours. A single daily CSV from one internal team does not need either discussion.