Data contracts between teams: the heuristic we use
The heuristic we use for data contracts between teams, and the point where it stops being true.
Somewhere between a principle and a habit sits the useful kind of rule: right most of the time, and wrong in ways you'll notice straight away. This is one of those.
The moment one team reads another team's tables directly, that schema is a public API without anyone agreeing to it. The producing team can't refactor and doesn't know it. Every painful data migration we've been called into started here.
THE ACCIDENTAL API
It starts reasonably. Someone needs a number, the data's right there, and a read-only credential takes five minutes. Six months later four dashboards, a nightly job and a machine learning pipeline depend on a column, and the team that owns it has no idea.
The consequence isn't a crash. It's paralysis. The owning team stops making changes they should make, because they can't establish who'd break, and "we can't refactor that table" becomes a permanent fact about the system.
WHAT A CONTRACT ACTUALLY CONTAINS
Less than people fear. The fields, their meaning in words, what's guaranteed non-null, how fresh it is, and how you'll be told when it changes. A page is usually enough. The value isn't in the ceremony, it's in there being a named surface that's smaller than the whole schema.
The freshness clause matters more than teams expect. "Yesterday's data by 6am" is a promise you can hold someone to; "roughly current" causes an argument during every incident about whether the number was even supposed to be right yet.
Checked against insurance claims: at about 15,000 claims a month it holds comfortably. It'd start to wobble an order of magnitude higher, where the fixed costs it ignores stop being small.
MAKING IT STICK
A contract nobody tests is a document. Assert it where the data leaves. Check that the fields exist, that the nulls are where you promised, that the row count is in a sane range, and fail the producing team's build, not the consumer's dashboard.
Then take away the direct database access. Leaving it available means the contract is optional, and an optional contract is the same as no contract on the day someone's in a hurry.
The rule would have caught a reserve was released twice against the same loss and didn't, because nobody applied it to the integration written under deadline. That's the usual story. The heuristic is fine, the coverage isn't.
WHERE IT GOES WRONG
- The owning team unable to refactor because nobody can enumerate the consumers.
- A freshness guarantee of "roughly current", relitigated during every incident.
- A contract with no assertions, so the first sign of a breach is a wrong number in a board pack.
- A read-only credential granted in five minutes, becoming an unversioned public API within a quarter.
If another team reads it, it's an API. Name it, promise something, and test the promise.
WHEN THE RULE FAILS
Treat it as a prompt to think, not a substitute for thinking. Its job is to stop the same conversation happening a fourth time, not to end it.