Lesson 13 · Pipeline patterns & best practices
Idempotency
The property that makes retries, backfills, and catchup safe.
Your win: explain what an idempotent task is, why Airflow requires it, and how to design one — the #1 data-pipeline best practice.
Airflow will rerun your task
Everything you learned in Part 2 reruns tasks: retries after a failure (L8), backfill over past dates (L6), catchup, and manual clears. So a task must be idempotent — running it again for the same data interval produces the same result, with no duplicate rows or side-effects.1
now(). Write to a deterministic destination keyed /
partitioned by that interval. Overwrite or UPSERT, never blindly append.
Then a rerun replaces the same output instead of doubling it.
datetime.today() (or now()) inside a task breaks
idempotency: rerun a failed 2024-01-01 run today and it processes today's data,
not Jan 1's. Use the DAG run's logical date / data interval (Lesson 5), which is stable
across reruns.
dim_*/fact_* tables for its interval (a
rebuild, not an append). Combined with catchup=False (Lesson 6), a rerun is
safe. This is the same instinct as Kafka's at-least-once + idempotency (Kafka L6)
and Postgres UPSERT (Postgres L4) — one principle across your courses.
Airflow Best Practices + Astronomer DAG best practices
Idempotency, avoiding top-level code, and templated fields — the foundational write-up.
→ airflow.apache.org — Best Practices
→ astronomer.io/docs/learn — DAG best practices
Check yourself (from memory)
Q1. An idempotent task is one where rerunning it…
Q2. Idempotency matters because Airflow…
Q3. An idempotent task should key its output on…
now() isn't — that's the classic bug.
now()); write to a
deterministic destination keyed/partitioned by that interval; OVERWRITE or UPSERT rather
than append. Our Spark transforms rebuild dim_*/fact_* tables per
run — idempotent by construction. (Same instinct as Kafka at-least-once + idempotency and
Postgres UPSERT.)