Always Use Explicit Schemas in PySpark
Schema inference is convenient in a notebook and dangerous in a pipeline. Here is how to define, enforce and evolve schemas deliberately.
PySparkBeginner2 min readUpdated 11 Oct 2026
Why inference hurts pipelines
spark.read.json(path) with inference has to scan the data to guess types, and the guess depends on whichever files happen to be present:
- A column that is always an integer today becomes a
stringthe day one record has"N/A" - Money inferred as
doubleintroduces floating-point rounding - An empty batch produces a schema with no columns, breaking downstream joins
Define the contract in code
from pyspark.sql.types import (StructType, StructField, StringType,
IntegerType, DecimalType, TimestampType, BooleanType)
ORDER_ITEMS = StructType([
StructField("order_id", StringType(), nullable=False),
StructField("line_id", StringType(), nullable=False),
StructField("product_id", StringType(), nullable=False),
StructField("quantity", IntegerType(), nullable=False),
StructField("unit_price", DecimalType(18, 2), nullable=False),
StructField("discount_amount", DecimalType(18, 2), nullable=False),
StructField("updated_at", TimestampType(), nullable=False),
StructField("is_deleted", BooleanType(), nullable=False),
])
df = (spark.read
.schema(ORDER_ITEMS)
.option("mode", "FAILFAST") # stop on malformed records
.json("s3://bucket/raw/.../dataset=order_items/"))mode options: PERMISSIVE (default — nulls plus a corrupt-record column), DROPMALFORMED (silently drops — rarely what you want), FAILFAST (raise).
Schema-first read
Raw JSONuntrusted
Explicit schemaStructType
FAILFASTmalformed? stop
Validaterequired fields
Typed DataFramesafe to join
Validate, then quarantine
required = ["order_id", "line_id", "product_id", "quantity"]
bad = df.filter(" OR ".join(f"{c} IS NULL" for c in required) + " OR quantity < 0")
good = df.subtract(bad)Evolving schemas
Version the schema alongside the manifest (schema_version). New optional fields can be added compatibly; removed or retyped required fields should fail the batch until the contract is updated on purpose.
Finished this note?
