2 min lesson
I/O managers: decouple compute from storage
Answer "Why use an I/O manager instead of writing the Delta path directly inside the asset function?" Use one lesson detail to support it.
Step 1 of 3
I/O managers: decouple compute from storagewhere data lands is config, not code
An I/O manager handles reading inputs and writing outputs so the asset function never hard-codes a path or a format. Your silver_events logic computes a DataFrame; a Delta I/O manager decides it gets written to a specific Delta table with the right options. Swap the manager and the same logic writes somewhere else, which keeps test, staging and prod clean.
- Compute logic stays pure: transform inputs, return outputs.
- Storage details (Delta path, partition columns, write mode) live in the I/O manager config.
- Different environments bind different managers - local Parquet in tests, governed Delta in prod.
Learn more
Full explanation
Asset checks: quality at materialization time
Asset checks: quality at materialization timefail the run, not the dashboard
An asset check is a data-quality assertion attached to an asset and evaluated when it materializes: not-null on a key, row count within a band, no duplicate event ids. A failing check can block downstream materialization, so bad data stops at silver instead of surfacing as a wrong number in a gold dashboard three hops later.
from dagster import asset_check, AssetCheckResult
@asset_check(asset=silver_events)
def no_duplicate_event_ids(context) -> AssetCheckResult:
dupes = count_duplicate_ids("silver_events")
return AssetCheckResult(
passed=dupes == 0,
metadata={"duplicate_ids": dupes},
)When the design deep dive asks where you put data-quality checks, name asset checks at the silver boundary and explain the blocking behavior: “I assert uniqueness and freshness on silver before gold reads it and I let the check failure block downstream materialization. That turns a silent wrong-number incident into a loud, scoped run failure with the bad metric attached.”