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.
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.
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.
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.
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.
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 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.
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.
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.
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.