A pipeline DAG (directed acyclic graph) shows the tasks of a batch job and the order they must run in. Each box is a task, each arrow says "run after this one succeeds", and there are no cycles, so the job always finishes. This sample is an Airflow-style daily job that extracts orders, customers and payments, validates them, builds two dbt models, loads Redshift and posts to Slack.
A DAG is not a UML diagram, though it looks like a simplified activity diagram. It is the native picture of every orchestrator: Airflow, Dagster, Prefect, Argo and dbt all render one.
validate_raw waits for all three (fan-in), then runs column checks (Airflow SQLColumnCheckOperator): no null keys, amounts non-negative, row counts within range.fct_orders joins to dim_customer, so it has an extra edge and runs second.load_redshift copies both models into the warehouse once both exist (fan-in again).The footer lists the DAG-level settings. They matter as much as the boxes: catchup=False stops Airflow from back-running every missed day, and max_active_runs=1 prevents two days overwriting the same tables at once.
Edges carry ordering only, not data; the data itself sits in S3 or the warehouse between tasks. Orchestrator UIs colour each task's border by its state in a given run, which turns the static graph into a run report. Arrange the graph left to right so time flows the same way readers expect.
Here the validation found 37 orders with a null order_id and failed after its two retries. Every task downstream is marked upstream_failed and never starts, which is exactly what you want: no bad data reaches the warehouse. A separate alert_on_call task uses trigger_rule=one_failed, so it runs only when its parent fails. Drawing the failure path explicitly (dashed red) makes reviewers ask the right question: who gets paged, and how do we re-run safely?
Mermaid flowchart LR draws the main graph:
flowchart LR
extract_orders --> validate_raw
extract_customers --> validate_raw
extract_payments --> validate_raw
validate_raw --> dim_customer
validate_raw --> fct_orders
dim_customer --> fct_orders
dim_customer --> load_redshift
fct_orders --> load_redshift
load_redshift --> notify_success
In Airflow the Python dependency operators are the source of truth, and the UI's Graph view renders them:
# Airflow 2.x: the same graph in Python (the DAG file IS the diagram source)
[extract_orders, extract_customers, extract_payments] >> validate_raw
validate_raw >> [dim_customer, fct_orders]
dim_customer >> fct_orders
[dim_customer, fct_orders] >> load_redshift >> notify_success
validate_raw >> alert_on_call # alert_on_call: trigger_rule="one_failed"