Inhaltsverzeichnis14 Abschnitte
- TL;DR
- Warum Teams Verträge ablehnen (und warum das falsch ist)
- Was ein Data Contract enthält
- Contract-Formate in der Praxis
- 1. YAML-Verträge (Open Data Contract Standard)
- 2. Schema Registry (Confluent / Karapace)
- 3. dbt-Contracts (dbt Core 1.5+)
- Was „Breaking" wirklich heißt
- Das Producer/Consumer-Protokoll
- Häufige Fehler
- Tooling-Landschaft
- Verträge und Exploration
- FAQ
- Fazit
Jeder Data Engineer kennt das: das Upstream-Team hat einen Column-Type geändert, ein Feld droppt oder eine Tabelle umbenannt — ohne Bescheid zu sagen. Deine Pipeline failt still um 3 Uhr morgens, das Dashboard zeigt Nullen, und das Business schiebt's auf Data Engineering. Data Contracts existieren, um genau das zu stoppen.
Ein Data Contract ist eine formale, versionierte Vereinbarung zwischen einem Daten-Producer (das Team, dem eine Tabelle oder ein Event-Stream gehört) und seinen Consumern (Pipelines und Apps, die davon abhängen). Stell's dir wie einen API-Vertrag vor, aber für Daten-Assets. Das Konzept ist nicht neu — Service-Teams nutzen OpenAPI-Verträge seit Jahren — aber die Anwendung auf Daten-Pipelines ist jung und reift noch.
TL;DR
- Data Contract = API-Vertrag für Daten: Schema, Semantik, SLA, Ownership.
- Drei Formate: YAML (ODCS), Schema Registry (Kafka/Confluent), dbt-Contracts.
- Breaking = jede Änderung, die einen funktionierenden Consumer brechen kann.
- Pflicht: Contract im Producer-Repo, CI-checked, Consumer-Registry.
- ROI: Weniger 3-Uhr-Alerts, schnelleres Debugging, Vertrauen zwischen Teams.
Warum Teams Verträge ablehnen (und warum das falsch ist)
Der übliche Einwand ist Overhead. Teams sorgen sich um Prozess, Dokumentation und gebremste Entwicklungsgeschwindigkeit. Diese Logik ist umgekehrt. Undokumentierte Schema-Changes kosten mehr — in Incident-Response-Zeit, Debug-Cycles und erodiertem Vertrauen zwischen Teams. Ein Vertrag erzwingt die Konversation bevor der Breaking- Change landet, nicht danach.
Der echte Overhead ist nicht Verträge schreiben. Es ist ihr Fehlen.
Was ein Data Contract enthält
Ein vollständiger Data Contract spezifiziert:
| Komponente | Beschreibung | Beispiel |
|---|---|---|
| Schema | Feldnamen, Typen, Nullability | user_id: INT NOT NULL |
| Semantik | Was jedes Feld tatsächlich bedeutet | „event_time ist UTC-Wall-Clock, nicht Server-Local" |
| SLA | Freshness- und Verfügbarkeitsgarantien | „Aktualisiert innerhalb 15 Min nach Event, 99,5% Uptime" |
| Ownership | Wer für den Datensatz verantwortlich ist | Team: Checkout Platform, Slack: #checkout-data |
| Versioning | Wie Änderungen kommuniziert werden | Semver: Breaking = Major, Additive = Minor |
| Quality-Regeln | Erwartungen, auf die Consumer setzen | „amount > 0 immer, currency immer ISO 4217" |
Contract-Formate in der Praxis
Es gibt noch keinen Single-Standard. Drei Ansätze dominieren in real-world Teams.
1. YAML-Verträge (Open Data Contract Standard)
Der Open Data Contract Standard (ODCS) definiert ein YAML-Schema für Data Contracts. Gewinnt an Traction bei Teams, die einen leichtgewichtigen, versionskontrollierten Ansatz wollen ohne Platform-Lock-in.
# ODCS-kompatibler Data Contract (vereinfacht)
apiVersion: v2.3.0
kind: DataContract
uuid: "a8b2c3d4-1234-5678-abcd-ef0123456789"
datasetName: orders
version: "1.4.0"
status: active
description:
purpose: "Order-Lifecycle-Events für Analytics und Downstream-ML"
usage: "Read-only. Felder mit 'internal' nicht nutzen."
team: checkout-platform
owner: platform-data@company.com
schema:
- name: orders
physicalName: checkout.orders
columns:
- name: order_id
logicalType: string
physicalType: VARCHAR(36)
required: true
description: "UUID v4. Immutable nach Creation."
- name: user_id
logicalType: integer
physicalType: BIGINT
required: true
- name: status
logicalType: string
physicalType: VARCHAR(20)
required: true
description: "Enum: pending, confirmed, shipped, delivered, cancelled"
- name: amount_usd
logicalType: number
physicalType: DECIMAL(10,2)
required: true
quality:
- rule: "amount_usd >= 0"
action: fail
sla:
- property: freshness
value: "15 minutes"
- property: completeness
value: "99.9%"
consumers:
- team: analytics-platform
contact: analytics@company.com
- team: ml-platform
contact: mlops@company.com
Die Datei lebt im Repo des Producer-Teams, versioniert und reviewed wie Anwendungscode. Änderungen daran triggern Notifications an registrierte Consumer.
2. Schema Registry (Confluent / Karapace)
Für Event-getriebene Architekturen auf Kafka erzwingt eine Schema Registry Verträge auf Protokoll-Ebene. Producer registrieren ein Avro-, Protobuf- oder JSON-Schema. Consumer decoden Messages mit dem registrierten Schema. Kompatibilitätsregeln werden von der Registry erzwungen — ein Producer kann buchstäblich keinen Breaking-Change publizieren, ohne die Contract- Version zu aktualisieren und den Compatibility-Check vorher zu bestehen.
# Schema-Version in Confluent Schema Registry registrieren
curl -X POST http://schema-registry:8081/subjects/orders-value/versions \
-H "Content-Type: application/vnd.schemaregistry.v1+json" \
-d '{"schema": "{\"type\":\"record\",\"name\":\"Order\",\"fields\":[{\"name\":\"order_id\",\"type\":\"string\"},{\"name\":\"amount_usd\",\"type\":\"double\"},{\"name\":\"status\",\"type\":\"string\"}]}"}'
# Kompatibilität checken bevor neue Schema-Version publiziert wird
curl -X POST http://schema-registry:8081/compatibility/subjects/orders-value/versions/latest \
-H "Content-Type: application/vnd.schemaregistry.v1+json" \
-d '{"schema": "<new schema JSON here>"}'
# Returns: {"is_compatible": true}
Schema Registry ist die stärkste Form von Contract-Enforcement. Trade-off: Tight-Kopplung ans Kafka-Ökosystem und zusätzliche Infrastruktur-Komplexität.
3. dbt-Contracts (dbt Core 1.5+)
Für Teams mit dbt macht das contract: enforced: true-Setting aus der
Schema-YAML einen Runtime-erzwungenen Vertrag. Wenn der Model-Output
nicht zu den deklarierten Spalten und Typen passt, failt der dbt-Run
laut, bevor die Daten Consumer erreichen.
# dbt schema.yml — Model mit Enforced-Contract
models:
- name: orders
config:
contract:
enforced: true
columns:
- name: order_id
data_type: varchar
constraints:
- type: not_null
- type: unique
description: "UUID v4. Immutable."
- name: amount_usd
data_type: numeric
constraints:
- type: not_null
- name: status
data_type: varchar
constraints:
- type: not_null
Der dbt-Ansatz ist ideal für den Transformation-Layer — er verhindert, dass ein Model-Refactor still Downstream-Consumer bricht, ohne zusätzliches Tooling.
Was „Breaking" wirklich heißt
Teams unter-spezifizieren das oft, was zu Streit führt. Ein Breaking Change ist jede Änderung, die einen vorher funktionierenden Consumer zum Fail oder zu falschen Ergebnissen bringen kann:
| Änderung | Breaking? | Notes |
|---|---|---|
| Spalte entfernen | Ja | Immer breaking |
| Spalte umbenennen | Ja | Immer breaking |
| Typ ändern (z.B. INT → VARCHAR) | Ja | Immer breaking |
| Nullability verschärfen (nullable → NOT NULL) | Ja | Lehnt vorher valide Zeilen ab |
| Neue NOT NULL-Spalte ohne Default | Ja | Bricht INSERT-Statements |
| Enum-Werte ändern | Ja | Bricht CASE/IF-Logik downstream |
| Neue nullable Spalte | Nein | Safe für die meisten Consumer |
| Nullability lockern (NOT NULL → nullable) | Nein | Safe |
| Index oder Constraint ohne Typ-Change | Nein | Transparent für Consumer |
Das Producer/Consumer-Protokoll
Ein Vertrag ohne Prozess ist nur Dokumentation. Minimaler Workflow, der in der Praxis hält:
Für Producer (Schema-Change-Protokoll):
- Jede Änderung, die Consumer brechen könnte, erfordert eine Contract- Version-Erhöhung vor dem Deployment.
- Breaking Changes brauchen Vorankündigung — definiere eine Lead-Time (z.B. zwei Sprints) im Vertrag.
- Additive Changes (neues nullable Feld) erfordern Minor-Bump und Consumer-Notification.
- Der Vertrag lebt im Producer-Repo; PRs dagegen triggern Notifications an alle registrierten Consumer.
Für Consumer:
- Als Consumer im Contract-File registrieren — so weiß der Producer, wen er notifizieren muss.
- Pipeline-Konfiguration auf eine spezifische Contract-Version pinnen.
- In Version-Upgrade-Notifications einschreiben (Slack, Jira, E-Mail).
- Undokumentierte Felder niemals als stabil behandeln.
Häufige Fehler
Verträge nur als Doku. Ein Vertrag, der von keinem Automated-Process gecheckt wird, ist ein Kommentar. Er driftet von der Realität ab. Wire den Contract in CI/CD, Schema-Registry-Kompatibilitäts-Checks oder dbt-Tests.
Semantische Definitionen skippen. Feldtypen sind der einfache Teil.
Der schwierige Teil ist, sich zu einigen, was event_time bedeutet —
Generated-Timestamp, Kafka-Ingestion-Time oder Warehouse-Landing-Time?
Semantische Fehlausrichtung verursacht still falsche Ergebnisse, die
schwerer zu debuggen sind als Schema-Fehler.
Kein Consumer-Registry. Wenn du nicht weißt, wer von einem Datensatz abhängt, kannst du sie nicht über Änderungen informieren. Eine Consumer-Liste im Contract-File ist die Minimum-Viable-Answer.
Keine Deprecation-Policy. Wie lange pflegst du v1.x nach v2.0? Definier das, bevor's unter Druck zur Verhandlung wird.
Verträge als Platform-Team-Problem behandeln. Verträge funktionieren, wenn jedes Team — auch App-Entwickler — Schema-Stabilität als ihre Verantwortung sieht. Wenn nur das Daten-Team es interessiert, schreibst du Verträge ins Leere.
Tooling-Landschaft
| Tool | Ansatz | Passt zu |
|---|---|---|
| Soda Core | YAML-Verträge + Quality-Assertions | dbt/Warehouse-Teams |
| Confluent Schema Registry | Protokoll-Level-Enforcement | Kafka/Streaming-Teams |
| dbt-Contracts (1.5+) | Model-Level-Enforcement | dbt-Transformation-Layer |
| OpenMetadata / DataHub | Catalog + Contract-Metadaten | Platform-Teams |
| Atlan / Collibra | Enterprise Data Governance | Größere Organisationen |
Kein universeller Gewinner. Wähl nach dem, wo deine Daten tatsächlich fließen und welches Tooling dein Team schon betreibt.
Verträge und Exploration
Wenn ein Contract gut definiert ist, wird ad-hoc-Exploration deutlich sicherer. Du kennst das Schema, die Semantik und die Quality-Garantien — Queries sind vorhersehbar. Harbinger Explorers Natural-Language-Interface funktioniert in diesem Kontext gut: wenn du beschreiben kannst, was die Felder eines Datensatzes tatsächlich bedeuten, generiert die AI SQL, das diese Semantik reflektiert, statt aus Column-Namen zu raten.
FAQ
Brauchen wir Data Contracts in einem 5-Personen-Daten-Team? Für die 2–3 wichtigsten Production-Datasets: ja. Ein YAML-File und dbt-Contracts kosten 1 Tag Setup. Für alles andere reicht klare Dokumentation.
Wie unterscheidet sich das von dbt-Tests?
dbt-Tests checken Werte (z.B. not_null, unique). dbt-Contracts
checken Schema (Spalten, Typen). Du brauchst beides.
Was, wenn der Producer kein Contract schreiben will? Schreib einen defensiven Contract auf Consumer-Seite: erwarte bestimmte Spalten und Typen, faile laut bei Drift. Schlechter als ein echter Producer-Contract, aber besser als Schweigen.
Ist Schema Registry DSGVO-relevant? Nicht direkt, aber sie hilft beim Art. 30-Verzeichnis: alle Daten- Schemata sind zentral dokumentiert und versioniert.
Fazit
Data Contracts verschieben die Konversation von „wer hat die Pipeline gebrochen" zu „wie evolvieren wir Daten safe". Sie erfordern Up-Front- Disziplin von Producern, zahlen sich aber aus in weniger Incidents, schnellerem Debugging und dauerhaftem Vertrauen zwischen Teams.
Klein anfangen: such einen kritischen Datensatz, schreib einen YAML- Vertrag, wire ihn in CI und registriere deine Consumer. Der Rest folgt.
Für Runtime-Quality-Validation, die checkt, ob Verträge eingehalten werden, siehe den Data-Quality-Testing-Guide.
Stand: April 2026. ODCS und dbt-Contracts entwickeln sich weiter — prüfe regelmäßig auf neue Features.
Geschrieben von
Harbinger Team
Cloud-, Data- und AI-Engineer in DACH. Schreibt seit 2018 über infrastrukturkritische Tech-Entscheidungen — keine Marketing- Folien, sondern echte Trade-offs aus Production-Workloads.
Hat dir das geholfen?
Jede Woche ein neuer Artikel über DACH-Cloud, Data und AI — direkt in dein Postfach. Kein Spam, kein Marketing-Sprech.
Kein Spam. 1-Klick-Abmeldung. Datenschutz bei Loops.so.