---
title: "Enforce a contract on every table other · Agent Wranglers"
url: https://agentwranglers.se/playbook/data-engineering/lineage-and-changes/output-contracts
id: DEN-41
level: Recommended
updated: 2026-10-27
build: a2a6bdfed830
tokens: 940
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Enforce a contract on every table other teams read

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Lineage and changes](https://agentwranglers.se/playbook/data-engineering/lineage-and-changes.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers.md). Updated 2026-10-27.

- ID: DEN-41
- Level: Recommended
- Chapter: [Lineage and changes](https://agentwranglers.se/playbook/data-engineering/lineage-and-changes.md)
- Rule: Declare the column names and types of each table other teams or tools read, and fail the build when the table no longer matches. A breaking change ships as a new version beside the old one.
- Check: Every table marked as shared in its metadata has an enforced contract. In CI, a change that renames a column of a contracted table fails the build, and a change that adds a column passes.
- How: Lineage shows who reads a table, and a contract states what they may rely on. Removing a column, changing its type or dropping a not-null constraint breaks readers. Adding a column does not. dbt model contracts, for example, check names and types before the build and flag breaking changes against the last release. Publish the new shape under a new version name. Keep the old one for the notice period in [Give notice before removing a column people use](https://agentwranglers.se/playbook/data-engineering/lineage-and-changes/deprecation-notice.md), then drop it. Contracts on the inputs follow [Treat each source schema as a contract](https://agentwranglers.se/playbook/events/contracts-and-schemas/source-schema-contracts.md).

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### YAML example

```yaml
models:
  - name: fct_orders
    config:
      contract:
        enforced: true
    columns:
      - name: order_id
        data_type: bigint
        constraints:
          - type: not_null
      - name: total_cents
        data_type: bigint
```

### Python example

```python
def breaking_changes(released: dict[str, str], proposed: dict[str, str]) -> list[str]:
    return [f"{column}: {kind} -> {proposed.get(column, 'removed')}"
            for column, kind in released.items() if proposed.get(column) != kind]
```

### JavaScript example

```js
export const breakingChanges = (released, proposed) =>
  Object.entries(released)
    .filter(([column, kind]) => proposed[column] !== kind)
    .map(([column, kind]) => `${column}: ${kind} -> ${proposed[column] ?? 'removed'}`);
```

## Sources

- [dbt model contracts](https://docs.getdbt.com/docs/mesh/govern/model-contracts)

## Related rules

- DEN-21 [Give notice before removing a column people use](https://agentwranglers.se/playbook/data-engineering/lineage-and-changes/deprecation-notice.md) in Data engineering
- EVT-5 [Treat each source schema as a contract](https://agentwranglers.se/playbook/events/contracts-and-schemas/source-schema-contracts.md) in Integration and events

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/data-engineering/lineage-and-changes/output-contracts">Agent Wranglers data engineering playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Backfill with the same code and record it](https://agentwranglers.se/playbook/data-engineering/lineage-and-changes/backfills.md)
- [Lineage and changes](https://agentwranglers.se/playbook/data-engineering/lineage-and-changes.md)
