Data Contracts as Types: How Bauplan Brings Compile-Time Schema Enforcement to Data Pipelines

August 14, 2026

Treating data contracts as types means enforcing a pipeline's schema at the type-system level, so a mismatch is caught before the pipeline runs rather than after it has written bad data. Most data pipelines are effectively untyped: they move data whose shape is only discovered at runtime, when something breaks. Bauplan is the execution layer for AI-generated data changes: you annotate a pipeline's schema in Python, and Bauplan checks it when it compiles the run graph, before any data moves.

TL;DR

  • "Data contracts as types" means schema enforced at the type-system level, not checked at runtime.
  • Typed Python (annotations, Pydantic) brought this to application code; most data pipelines never got it.
  • Bauplan annotates schema in Python and validates it at run-graph compilation, before execution.
  • This differs from schema registries (serialization boundary) and runtime validation (after the write).
  • The model is grounded in peer-reviewed work: "Building a Correct-by-Design Lakehouse," PaPoC '26, EuroSys.

What "Data Contracts as Types" Means: Schema Enforcement at the Type System Level

A data contract is an agreement about a dataset's schema and shape. Treating it "as types" means expressing that contract the way a programming language expresses types, so it is enforced by a checker rather than by a runtime assertion. The difference is when a violation surfaces: a type error is caught before the program runs, while a runtime check fires only once execution reaches it, with data already in motion. Applied to pipelines, contracts-as-types means the schema a step must produce is verified before the step executes.

The Python Analogy: Typed Functions and Pydantic vs Untyped Data Pipelines

Python developers already know this shift. Type annotations and Pydantic let you declare the shape of your data, and tools catch mismatches before the code runs or at well-defined boundaries, instead of deep in production. That change made application code dramatically more reliable.

Data pipelines mostly never got it. A typical pipeline reads a table, transforms it and writes it, with the schema assumed rather than enforced. When an upstream column changes type or disappears, nothing catches it until the pipeline runs and fails, or worse, succeeds and writes wrong data. Contracts-as-types brings the typed-Python discipline to the pipeline itself.

How Bauplan's Typed Contracts Work: Annotate Schema in Python, Caught at Run-Graph Compilation

In Bauplan, you annotate the schema a pipeline step expects and produces directly in Python. Before executing, Bauplan compiles the pipeline into a run graph, and it checks those schema contracts at that point. A mismatch, a wrong type, a missing column, a broken hand-off between steps, fails at compilation, before any data is read or written. The violation is a build-time error with a clear message, not a 3 a.m. production incident. Execution only proceeds on a graph whose contracts are satisfied.

Data Contracts as Types vs Schema Registries (Confluent, Avro, Protobuf)

Schema registries enforce a schema at the serialization boundary, mainly in streaming: a message must match its registered schema to be produced or consumed. That is valuable for message compatibility, but it governs the wire format of individual messages, not the correctness of a transformation across a pipeline. Registries sit at the edge of the system; Bauplan's typed contracts sit inside the pipeline, enforcing the schema each transformation step must produce.

Data Contracts as Types vs Runtime Validation (Great Expectations, Pandera)

Runtime validation tools check data during or after execution. They are expressive and strong at content rules, but by the time a check runs, the data exists, so a failure is something you react to. Typed contracts catch structural violations earlier, at compilation, before execution. The two are complementary: types handle schema before the run, runtime checks handle content during the audit. Bauplan uses both, but the "as types" part is the compile-time layer runtime validation cannot provide.

Typed Data Pipelines in Python: Schema Enforcement at the Execution Level

"Typed Python pipelines" often points to typed data-processing frameworks, Apache Beam's typed PCollections, or Dagster's asset types. Those are real typing, but of different things. Beam's PCollection types describe elements flowing through a processing graph; Dagster's asset types annotate assets in an orchestration graph. Bauplan types the schema of the data a pipeline step produces and enforces it at execution-graph compilation, so the guarantee is specifically about the shape of the tables the pipeline writes, not the element type in a stream or a metadata annotation on an asset. That is what "schema enforcement at the execution level" means here.

Frequently Asked Questions

What is the difference between data contracts and schema validation?

Schema validation typically checks data against a schema at runtime, after the data exists. A data contract is the agreement itself, plus how it is enforced. Treated as types, a contract is enforced before execution, at compile time, so a schema violation stops the pipeline before it runs rather than being flagged after a write. Validation tells you data was wrong; a typed contract prevents the wrong-shaped run from happening.

How are data contracts different from Pydantic models?

Pydantic validates data against a model at runtime, usually at the boundaries of an application, and is excellent for that. A Bauplan typed contract enforces the schema of a pipeline step at run-graph compilation, before the pipeline executes. Pydantic is the right analogy for the idea, declare the shape, enforce it, but Bauplan applies it to pipeline execution and checks it ahead of the run rather than as data passes through at runtime.

Implementing data contracts as types for scalable production data

At scale, the value of contracts-as-types is that violations are caught before execution, so a schema break in one pipeline cannot silently write bad data that many downstream consumers then read. In Bauplan you annotate each step's schema in Python and the contract is enforced at compilation, and because execution also runs on isolated branches with an audit gate, a passing contract is combined with validated, atomic publication. For large production estates, that moves failure from runtime incidents to build-time errors.