---
title: "Directed acyclic graph (DAG)"
description: "A directed acyclic graph (DAG) is a graph of nodes connected by one-way edges with no cycles, used to represent task dependencies so that execution order is unambiguous."
canonical: "https://motherduck.com/glossary/directed-acyclic-graph/"
related:
  - title: "Orchestration | MotherDuck Docs"
    url: "https://motherduck.com/docs/integrations/orchestration/"
  - title: "Data Engineering Is AI Engineering | MotherDuck"
    url: "https://motherduck.com/videos/data-engineering-ai-engineering/"
  - title: "DuckLake Architecture Deep Dive"
    url: "https://motherduck.com/blog/ducklake-architecture-deep-dive/"
---

# Directed acyclic graph (DAG)

> A directed acyclic graph (DAG) is a graph of nodes connected by one-way edges with no cycles, used to represent task dependencies so that execution order is unambiguous.

## Overview

A directed acyclic graph, or DAG, is a mathematical structure made of nodes and directed edges (arrows) where following the arrows can never lead back to a node you've already visited. "Directed" means each edge has a direction—A depends on B, not the other way around. "Acyclic" means there are no loops: if you could get from A back to A by following edges, the graph would be invalid for scheduling purposes, because it would imply a task depends on itself, directly or indirectly.

DAGs are the standard way to represent dependencies in data pipelines, build systems, and workflow orchestrators. If task C needs the output of tasks A and B, the DAG has edges A → C and B → C, and any valid execution order runs A and B before C.

<glossary-callout guide="duckdb-cheatsheet-full" />

## Why it matters in data engineering

Orchestration tools like Airflow, Dagster, Prefect, and dbt represent an entire pipeline as a DAG:

- **Airflow** DAGs are Python objects where each node is an operator (a task) and edges are set with `>>` or `.set_downstream()`.
- **dbt** builds a DAG automatically from `ref()` calls between models—if `stg_orders` references `raw_orders`, dbt knows to build `raw_orders` first.
- **Spark** and other query engines compile a query plan into a DAG of stages, scheduling parallel work where the graph allows it and serializing work where it doesn't.

The acyclic property is what makes a pipeline schedulable at all: a topological sort of the DAG produces a valid run order, and any node with no unmet dependencies can run immediately, which is what allows independent branches of a pipeline to execute in parallel.

```text
raw_orders ──▶ stg_orders ──▶ fct_orders ──▶ dashboard_orders
raw_customers ─▶ stg_customers ─┘
```

Here `stg_orders` and `stg_customers` can build in parallel since neither depends on the other; `fct_orders` waits for both.

## Detecting cycles

If you accidentally introduce a circular dependency—model A references model B, which references model A—most orchestrators will refuse to run and raise a cycle-detection error rather than deadlocking or looping forever. This is a deliberate safety property of DAG-based tools: a cycle has no valid execution order, so it's caught at compile/parse time instead of at runtime.

DuckDB itself doesn't expose DAG concepts directly, but tools built on top of it—like dbt-duckdb—use the same DAG model as any other dbt adapter, resolving `ref()` dependencies between DuckDB tables/views before executing each model in order.