The Serving Contract
Last reviewed · content updated
IntermediateWhat you'll learn
~18 min- Write a serving contract covering shape, freshness, availability, and change policy
- Classify a schema change as additive, breaking, or semantically breaking
- Run a deprecation that gives consumers time rather than notice
The moment the obligation starts
The first time someone else builds on your output, you have consumers. Not when you announce it — when they depend on it.
From that point every change you make is a change to their system. The serving contract is what makes that relationship explicit instead of discovered.
You inherited exactly this in Module 2: a curated table whose column names did not match its contents, whose freshness claim was untested, and whose meaning nobody had written down. Everything that made that painful is what you now owe the next person not to do.
Prompt first: draft the contract, classify the changes
Here is my table definition and the consumers I know of [paste].
Draft a serving contract with four clauses: shape (columns, types,grain, and where NULL is legitimate), freshness (when the period isavailable and how the actual cut is exposed), availability (whathappens on load failure), and change policy.
Then classify each of these as additive, breaking, or semanticallybreaking, and give the notice each requires: - adding a customers_served column - renaming total_duration_seconds - changing the population to exclude municipal accounts - changing the threshold from 3.5 to 4.0
For the last two, tell me what a consumer would observe.Those last two are the teaching cases. An agent will usually classify them correctly, and the “what would a consumer observe” answer — nothing; their numbers just change — is the one worth having in front of you when you are tempted to ship a definition tweak quietly.
Stop and escalate when you find a consumer you cannot name — attribution from warehouse query logs is a platform-team ask, and a breaking change with unknown consumers is a change you do not yet have the standing to schedule.
You change the population rule to exclude municipal accounts. Schema is identical, every consumer keeps running, no errors. What kind of change is this and what notice does it need?
The four clauses
SERVING CONTRACT - circuit_monthly
SHAPE one row per energized circuit per month circuit_id TEXT, month DATE, degradation_score NUMERIC, fault_count INTEGER, total_duration_seconds INTEGER, status TEXT ('scored' | 'no data'), data_as_of TIMESTAMP degradation_score is NULL when status = 'no data'
FRESHNESS previous month available by the 2nd of each month, 06:00 America/Chicago. data_as_of on every row states the actual cut.
AVAILABILITY if the load fails, the previous month's data remains and data_as_of does not advance. Stale data is never silently republished as current.
CHANGE additive changes ship with 1 week notice. Breaking changes ship with 30 days notice and a parallel period. Owner: Distribution Planning Supervisor. Consumers of record: crew-assignment report, rate analysis workbook, exec reliability tile.Consumers of record is the clause people leave out and regret. You cannot give notice to consumers you cannot name. Maintaining that list — even informally, by asking who reads this — is what converts a breaking change from an incident into a scheduled task.
Three kinds of change
Additive — a new column, a new row where none existed. Safe for consumers that select what they need. Not safe for anything doing SELECT * into a fixed-width structure, which is why the notice period is short but not zero.
Breaking — renaming a column, changing a type, changing the grain, removing a value. Consumers break loudly and immediately. Unpleasant, and the good case, because everybody finds out at once.
Semantically breaking — the schema is unchanged and the meaning moved. The population rule now excludes municipal accounts. The threshold changed from 3.5 to 4.0. Nothing breaks. Every consumer keeps running, and every number quietly means something different than it did last month.
A breaking change announces itself. A semantically breaking change does not, and it is far more common — a definition adjustment, a corrected filter, a threshold retuned.
Treat semantic changes with the same notice period as breaking ones, and require the version marker below. The impulse to skip announcement is strongest exactly here, because nothing appears to break.
Versioning the meaning
The cheapest mechanism that works: a definition version carried in the data.
definition_version 3One integer, incremented whenever any of the five definition elements from Lesson 1.3 changes, with a changelog next to the definition. A consumer comparing March to September can now see that the meaning changed between them, without knowing anything about your release process. It also answers half of Lesson 4.4’s reproducibility question for free.
Deprecation that gives time, not notice
The difference is whether the consumer can act.
Notice: “This column is removed on the 30th.” Consumer scrambles.
Time: the new column ships alongside the old one; both are populated for the parallel period; the old column is marked deprecated in the catalog and in the contract; a reminder goes out at the halfway mark; the old one is removed only after you have confirmed the named consumers migrated.
The parallel period is what makes it work. Migration happens on the consumer’s schedule instead of yours, and you find the consumer you did not know about — because they are the one still reading the deprecated column when you check.
Key takeaway
You have consumers from the moment someone depends on your output, not from when you announce it. The contract has four clauses — shape, freshness, availability, change policy — plus the consumers-of-record list that makes notice possible at all. Additive and breaking changes announce themselves; semantically breaking ones do not, which is why they need the same notice period, a definition_version in the data, and a changelog. And deprecate by giving time rather than notice: run the old and new in parallel, and use the parallel period to find the consumer you did not know you had. Lesson 5.3 covers getting results out of the boundary.