Data and schema changelogs: announcing changes to tables other people query

Somewhere right now, a query you have never seen is reading a table you are about to change. Your database, your warehouse, your event stream — each one is an interface, and unlike an API it has no type-checker at the boundary, no compiler on the consumer’s side, and no way to grep for callers. The analyst’s saved query, the BI dashboard, the scheduled export, the other team’s service parsing your events: none of that SQL lives in your repo. When you rename a column, you break code you cannot see, on a schedule you don’t control. This guide covers who actually consumes your schema, what counts as a breaking change in data (more than you think), how to write the entry, how to retire a column without an outage, and what to say when a backfill changes history.

Your schema has users you can’t see

An API team can at least watch request logs and know which endpoints matter. A data team’s consumers are quieter: dashboards that refresh at 6am, month-end report jobs, a marketing tool syncing a table nightly, an ML pipeline reading features, a spreadsheet someone in finance connects directly to the warehouse. Hyrum’s Law applies with the volume turned up — with enough consumers, every observable property of your data becomes something someone depends on: the column’s name, its type, its timezone, whether it can be null, even the fact that there’s exactly one row per order.

That is why “it’s just an internal table” is the famous last words of data engineering. The moment a second team can query it, it has users, and changes to it need announcing like any other interface change.

Data breaks silently — and later

Code that calls a removed API endpoint fails loudly, at the moment of the change. Data has a crueler failure mode: the query keeps running and the numbers go wrong. Change a table’s grain from one-row-per-order to one-row-per-line-item and every SUM(revenue) downstream quietly doubles. Nothing errors. The dashboard renders. The wrongness is discovered weeks later by whoever trusts the number most — often in a month-end meeting — and at that point the damage isn’t one bad chart, it’s that people stop trusting the warehouse.

The delay matters too. A migration runs in seconds, but the breakage arrives at query time: the next dashboard refresh, the next scheduled report, the next quarter-close. By then the change has scrolled out of everyone’s working memory. The first question in every data-debugging session is “did anything change?” — and a dated, searchable changelog is the only good answer. The person investigating a metric jump on August 4th will search for the table name; if the entry names orders_daily.revenue_usd exactly, they find their answer in one minute instead of one afternoon of git archaeology across repos they don’t own.

What counts as breaking (more than you think)

The obvious ones — dropping or renaming a table or column, changing a type — break queries loudly, which ironically makes them the safe kind. The dangerous changes are the ones queries survive:

  • Semantic changes. Same column name, new meaning: revenue_usd now nets out refunds, active_user now requires two sessions instead of one. Every downstream query still runs and every result is now subtly wrong. This is the single most important thing to announce, and the least likely to appear in a migration file.
  • Grain changes. One row per order becomes one row per order-item. Every unguarded SUM and COUNT downstream inflates.
  • New enum values. A new status = 'partially_refunded' means every CASE WHEN written against the old list silently drops those rows into nothing. In data, additive is not automatically safe — the API-changelog rule about sneaky-additive changes applies double.
  • Nullability changes. NULLs appearing in a column that never had them quietly vanish rows from joins and aggregates.
  • Unit and timezone changes. Milliseconds to seconds, local time to UTC, cents to dollars. The query runs; the number is off by a factor nobody sees coming.
  • Filter and dedup logic in derived tables. “We now exclude internal test accounts from events_clean” changes every metric built on it.
  • New columns, sometimes. Harmless — except to SELECT * consumers, strict-schema loaders, and anyone syncing the table elsewhere. Cheap one-line entry; write it.

The entry: exact names, exact effects, exact dates

A schema-change entry is a translation job: the migration file says what the database did; the entry says what the readers’ queries will do. Name tables and columns exactly (that’s what people search for), quantify how numbers move (the exact before-and-after discipline from game patch notes transfers directly), and date the entry for when the change lands in the data, not when the PR merged:

2026-08-04 — orders_daily: revenue_usd now excludes refunds  [breaking]

What changed: orders_daily.revenue_usd nets out refunds
(was: gross of refunds). Name and type unchanged.

Why: aligns the warehouse with finance’s definition. The gross
figure is still available — new column revenue_gross_usd.

What breaks: anything summing revenue_usd and expecting gross.
Expect reported revenue roughly 2–4% lower.

History: backfilled to 2024-01-01, so past periods change too —
Q2 2026 moves from $1.94M to $1.88M. Re-pull any local extracts.

Action: switch to revenue_gross_usd if you need the old number.
Questions: #data-changes.

Four things earn their place in that template: the exact identifier (searchable), the direction and size of the movement (so people can sanity-check their own dashboards), the backfill scope (see below), and a named place to ask questions. If you can’t fill in “what breaks,” you haven’t finished thinking about the change — that line is for you as much as for the reader.

Renaming and removing columns: deprecation, data edition

The deprecation playbook maps cleanly onto schemas, with one bonus: databases give you compatibility shims for free.

  1. Add the new alongside the old. New column or table with the new name or semantics; populate both during the window (dual-write, or compute both in the model).
  2. Alias the old to the new. CREATE VIEW old_name AS SELECT … is the data equivalent of a 301 redirect — old queries keep working while their owners migrate on their own schedule.
  3. Announce with a removal date. One entry at deprecation, a reminder near the end, and a final entry when it drops — the same announce-three-times rhythm as any breaking change. Dates, not “soon.”
  4. Check the logs before dropping. Warehouses hand you what API teams have to build: query history. If something still selects from the deprecated view the week before removal, you know exactly which job — go talk to its owner instead of breaking them.

Backfills and restatements: when history changes

Analysts assume yesterday’s numbers don’t move. Any job that rewrites past rows breaks that assumption, and it deserves a name borrowed from finance: a restatement. The entry states which periods changed, in which direction, by roughly how much, and why — “recomputed attribution for March–June; paid-channel conversions shift up ~1%, totals unchanged.” Skip it, and the first person who notices that a screenshot from last month disagrees with the same dashboard today concludes the data can’t be trusted — and starts keeping their own copies, which is how a company ends up with six versions of revenue.

Found bad data and fixed it? That’s a hotfix entry: symptom first (“signup counts for July 10–12 were undercounted by ~8%”), then the fix, then whether history was corrected.

Event schemas and customer-facing data

Everything above gets stricter when the schema leaves the building. Webhook payloads, analytics events, Kafka topics, public datasets, warehouse-sync integrations — these are API contracts whose consumers are programs. Version the event contract explicitly, treat new required fields and type changes as majors, and give the machine layer (JSON Schema, a schema registry) a human layer next to it: a machine-readable changelog lets customers’ pipelines watch for changes mechanically while their engineers read the same entries in prose. If customers pull your tables directly, schema changes are public breaking changes with real windows — internal-memo treatment is how you end up in their incident reports.

Where these entries live

For most teams the audience is internal, which makes this a classic internal changelog: one append-only stream for the warehouse, mirrored into a #data-changes channel as a pointer, not an archive — chat scrolls away precisely on the timescale at which query-time breakage arrives. Column descriptions and dbt docs describe the current state; the changelog holds the transitions — the same two-tenses split as docs versus changelog everywhere else. And because entries are born in migration PRs, the natural workflow is to draft the entry alongside the migration and have CI post it when the migration actually runs in production — which also stamps the entry with the date the data changed, not the date the code merged. A nice side effect: a complete, dated history of schema changes is exactly what a change-management audit wants to see.

Six anti-patterns

  • The DDL dump. Pasting the migration — ALTER TABLE orders ADD COLUMN … — as the entry. That’s the diff, not the announcement; it tells no one what their queries will do differently.
  • The silent semantic change. Name stays, meaning changes, nobody told. The worst one, because nothing errors and everything is wrong.
  • Rename without an alias or window. Free compatibility views exist; breaking every downstream query overnight anyway is a choice.
  • Announced only in standup. Query-time breakage arrives weeks after the meeting ends. If the announcement has no permalink, it doesn’t exist when the debugging starts.
  • The unannounced backfill. Past numbers moved; the first person to notice a mismatch with an old screenshot now distrusts every dashboard you serve.
  • “Additive is always safe.” New enum values, new NULLs, SELECT * consumers. The entry costs one line; the silent undercount costs a quarter of bad decisions.

Running a data changelog on Wakelog

Wakelog fits this shape well: an unlisted project gives the warehouse a dated, append-only stream with per-entry permalinks — paste them into dbt descriptions, PR reviews, and “why did this metric move?” threads. Tags separate breaking grain changes from routine improved model updates; the API and CLI mean your migration pipeline can post the entry the moment the migration runs (draft it at PR time, publish at run time); the per-project webhook mirrors every entry into Slack or Discord; and RSS + JSON feeds let both analysts and scripts subscribe. The honest caveat: Wakelog doesn’t connect to your warehouse, diff your schemas, or detect drift — pair it with whatever runs your migrations. It’s the announcement layer: the place the humans (and their dashboards’ defenders) actually read.

Give your schema changes a permanent record   Next: internal changelogs →

Related guides

  • Data pipeline changelogs: release notes for dbt models and Airflow DAGs
    A pipeline’s consumers never chose a version and can’t pin one — the 9am dashboard just reads whatever last night’s run produced. That makes every schedule change, lookback tweak, and DAG rename a change to somebody’s morning, delivered silently. What a pipeline changelog announces, who reads it, and the entry template that keeps analysts and on-call engineers trusting the numbers.
  • AI model changelogs: announcing updates your users can’t diff
    A model update changes behavior, not features. Pinned snapshots and loud alias moves, evals as evidence, the silent-swap trap, and retiring versions without burning users.
  • Webhook and event payload changes: announcing new shapes to consumers who can’t pin a version
    An API consumer chooses when to call you, which version to ask for, and when to upgrade. A webhook consumer wrote a handler during integration week three years ago and hasn’t looked at it since — and whatever your producer sends tonight is what that handler receives, ready or not. Changing a payload you push needs different disciplines than changing an endpoint people call, starting with the fact that you — uniquely — hold a complete list of everyone who will break.

Last updated 2026-07-29 · All guides