Pipeline DAG Diagram

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.


1. The Diagram

DAG daily_orders: extract, validate, transform, load, notifyextract_ordersS3 export, @taskextract_customersS3 export, @taskextract_paymentsStripe API, @taskvalidate_rawSQL column checksdim_customerdbt run --selectfct_ordersdbt run --selectload_redshiftS3ToRedshiftOperatornotify_successSlackWebhookOperatorschedule="@daily" catchup=False max_active_runs=1 retries=2 retry_delay=5mEach arrow means: run after the upstream task succeeds (trigger_rule=all_success).Tasks in the same column have no edge between them, so the scheduler may run them in parallel.
  1. Extract (column 1). Three independent tasks copy yesterday's rows to S3. No arrows connect them, so they run in parallel.
  2. Validate. validate_raw waits for all three (fan-in), then runs column checks (Airflow SQLColumnCheckOperator): no null keys, amounts non-negative, row counts within range.
  3. Transform. Two dbt models. fct_orders joins to dim_customer, so it has an extra edge and runs second.
  4. Load. load_redshift copies both models into the warehouse once both exist (fan-in again).
  5. Notify. A Slack message confirms the run. Because it is last, a green Slack message means the whole chain succeeded.

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.

2. How to Read It

Notation: task border = run statesuccessfinished OKrunningin progressfailederror after retriesupstream_failedparent failedskippedbranch not takendependency: upstream >> downstream (no data on the edge)

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.

3. Variant: A Failed Run

Run 2026-10-01: validation fails, failure branch firesextract_ordersS3 export, @taskextract_customersS3 export, @taskextract_paymentsStripe API, @taskvalidate_rawSQL column checksdim_customerdbt run --selectfct_ordersdbt run --selectload_redshiftS3ToRedshiftOperatornotify_successSlackWebhookOperatoralert_on_calltrigger_rule=one_failednull_count(order_id) = 37check failed after 2 retries

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?

4. Pros, Cons, and When to Use

Pros

Cons

When to use

When not to use

5. As Diagrams-as-Code

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"