madeofdoors
The essay

The Phenomenology of the DAG

← back to the doors

A felt poetics, held to technical exactness.

How to read this

The sections below form a dependency graph, not a list. Each one is a node; each builds only on what came before it, and the order is the only legal flattening I could walk that honors every edge. We start by meeting the object (Encounter), learn how it becomes visible (The look), then spend the longest stretch on the eleven verbs — the centerpiece, the widest tier — before descending into what it feels like to inhabit one (The felt core), where they are lived (In the hands), and finally what the whole calm structure cost to build (The tensions). Read top to bottom and you are following edges. The insights and questions at the end are the sink: every section drains into them.


1. Encounter — what a DAG is

A directed acyclic graph is, before anything else, a claim about time. Not clock time — something more fundamental: the impossibility of return. You draw an arrow from A to B and you are asserting that A precedes B in some irreversible sense. The word acyclic names an absence, and that absence is everything: no path leaving any node can circle back to visit it again. The figure has no memory of itself the way a cycle would require. It moves, strictly, forward.

That absence purchases something precise. A topological ordering always exists in a DAG — and exists if and only if the graph is acyclic. These are not two facts but one fact stated from opposite ends. What the topological sort hands you is not the ordering but one legal flattening of many; most real DAGs admit hundreds or millions of valid orderings, all superimposed, and any single sort collapses that superposition into one walked line.

Inside the figure, two substructures compete for your attention. A chain is a sequence where each node is reachable from the one before — a lineage, a dependency spine with no branching. An antichain is its opposite: a set of nodes in which no node can reach any other. The members of an antichain are mutually incomparable; they do not know about each other, and that mutual ignorance is exactly what makes them schedulable at once. The reachability relation — can A reach B by following edges? — is a strict partial order: irreflexive, asymmetric, transitive. The DAG is this order made visible.

So you are not introduced to a network. You are handed a flattened poset, a piece of frozen causation, and the first thing it asks is that you accept there is no walking it back.

2. The look — how we come to see it

A DAG in raw form has no shape at all. It is pure relation: nodes and directed edges, no position assigned to anything. The partial order exists before any drawing does. What makes it seeable rather than merely conceivable is the moment someone commits those relations to a plane and gives every node a place — and that commitment is not neutral. Most of what we call "the structure" of a pipeline or a build graph is really the structure of its drawing.

The reason DAGs look the way they do has a name: the Sugiyama framework, introduced by Kozo Sugiyama and colleagues in 1981. It is a layered-drawing pipeline, and nearly every tool that renders a DAG — Graphviz's dot, d3-dag, every CI pipeline view you have squinted at — either implements it or inherits its logic. It runs in four passes. Rank assignment sorts nodes into layers so every edge points one way across boundaries — this is the step that makes acyclicity visible. Crossing minimization orders nodes within each layer to reduce edge crossings; because doing this optimally is NP-hard, the framework leans on heuristics, most often the barycenter or median method. Coordinate assignment fixes absolute positions, aligning connected nodes and straightening long edges. Edge routing draws the long spans through the dummy nodes inserted earlier.

When those passes succeed, the drawing reads downhill: the eye enters at the sources and slides to the sinks without resistance. This is not a metaphor imposed on the figure; it is the perceptual consequence of consistent edge direction. And it cuts both ways. A tangle — crossings everywhere, edges stretched across many layers — reads as anxiety that is not metaphorical. The visual cortex genuinely labors to trace a crossing-heavy figure, forced into serial attention where the structure should have allowed parallel grasp. Cleanliness, then, is not decoration. It is a report on whether the dependencies are well-separated.

To see a DAG well is already to begin handling it. Which is the next move.

3. The eleven verbs

Here is the centerpiece — the widest tier of this graph, eleven incomparable nodes you may grasp in any order, though I lay them in Cruz's. Each is a way the hand meets the figure.

Encounter. What you meet first is not nodes or edges but a claim about irreversibility — the figure announces, before you read a label, that it moves one way and does not come home. To encounter a DAG is to feel acyclicity as a fact of the body: every arrow is congealed because, a fossilized decision that this had to precede that.

Use. You do not consult a DAG the way you consult a map — you run it, and it runs whether or not you understand it. In Make, in Spark, in a spreadsheet's hidden recompute, the graph is not a picture of the plan but the plan itself, the thing the system reasons over while you only touch a leaf or a root. To use a DAG is to discover the structure was load-bearing all along: change one value and the engine fans the dirty mark across the exact transitive closure, no more, no less.

Work with. Working with a DAG means negotiating with two measurements that answer to nothing you wish. Its width offers room — parallelism, hands you can add, an antichain wide enough to spread load across — and its depth refuses you, because the critical path is the longest chain and no quantity of help compresses a sequence whose every link must wait on the last. You spend resources against width; depth you can only endure.

Follow. To follow is to commit to a single legal flattening and walk it, knowing the DAG holds millions of others superimposed behind your one chosen line. A topological order exists precisely because the graph is acyclic — the two facts are one fact — but the sort reveals only that a linear reading is possible, never which is correct. Following collapses the superposition: your finger traces one honored sequence while the structure quietly keeps every other valid path.

Traverse. Traversal teaches that the same skeleton runs in two directions, and the direction is the meaning. The forward pass carries values down toward the sinks; the backward pass — reverse topological order, the chain rule accumulating at each node — carries sensitivities back up the identical graph. To traverse is to feel that a DAG is not a path but a reversible reading of an irreversible object: same structure, opposite grain.

Sense. Before you measure anything, you can see whether a DAG is sound, because a clean Sugiyama drawing reads downhill — the eye enters at the sources and slides to the sinks, antichains resting as legible horizontal bands. A tangle reads as anxiety that is not metaphorical: the visual cortex genuinely labors over a crossing-heavy figure. Sensing a DAG is letting calm and confusion become information.

Touch. When you reach into a DAG to touch one thing, you reach for the critical path — the spine that sets the floor on everything. It is the part that has hardened into fate: not how much there is to do, but how long the doing must take no matter how many workers you throw at it. To touch the critical path is to lay a hand on the one chain you cannot dig below.

Orchestrate. To orchestrate is to keep the frontier saturated — to ensure that at every instant, every node whose dependencies are satisfied is running, and no worker idles while work exists. Fan-out releases the whole ready antichain at once; the barrier holds until an entire tier resolves; the pipeline chains in series. Orchestration is not comprehension of the totality but the discipline of feeding a moving edge — the same scheduling problem whether the nodes are build targets, Airflow tasks, or inference calls.

Hold. The quiet truth is that you never hold the graph — you hold only its frontier, the set of currently-ready nodes whose predecessors are all done. That set is an antichain: its members are mutually incomparable, so you may grasp them in any order or all together, and as you complete each one it dissolves and exposes the rank behind it. You stand always at a waterline, never at the sea. What the figure offers your hand is forever the rim of the presently-possible — affordance, not grasp.

Encircle. You may want to draw a clean ring around a node — everything related — but the DAG forbids the circle, because a closed loop is exactly the cycle acyclicity rules out. What it grants instead is the cone: sweep every edge backward for the full set of ancestors, forward for all descendants, and you have the only honest encircling the structure permits. Relatedness here is directional — a node is bound to its causes and its effects but not to its siblings on the same rank.

Layer. To layer a DAG is to assign every node a rank so that all edges point strictly across boundaries in one direction — the move that makes acyclicity visible and the drawing readable. Strip every redundant edge first, keep only the transitive reduction — which, because the graph is acyclic, is unique — and the layered figure becomes a Hasse diagram: the transitive reduction drawn with edges oriented consistently toward their covers. Not a simplification but the identical poset wearing only what it must, every implied relation still present, encoded in transitivity rather than drawn.

Eleven verbs, and notice what they share: almost none of them is comprehend the whole. They are all ways of touching an edge, a frontier, a chain, a cone. That is not a limitation of the hand. It is the shape of the thing — which the next section is about.

4. The felt core

Run a finger along any edge and you feel the grain of it: the edge points. It is not a road but a one-way membrane — you may pass from cause toward effect, never the reverse. This is what acyclicity is to the body before it is a theorem: irreversibility made visible. A cycle would let you return to where you began, dissolving before and after into a smear; the acyclic condition forbids exactly that homecoming.

Stand at a node and look back along its incoming edges. Each is a sentence in the second person: I rely on you having finished. A dependency is not information; it is obligation. The arrow is a promise extended backward and a debt owed forward — the node cannot begin until its predecessors have made good. To wire an edge is to consent to wait, to stake your readiness on another's completion. A dependency graph feels less like a map than like a ledger of trust.

And here is the truth that organizes all eleven verbs: you never hold the graph, only its frontier — the set of nodes whose dependencies are all satisfied, the doable-now. It is an antichain, graspable in any order or all at once, and it advances like a shoreline as you complete what sits on it, each finished node dissolving to expose the next rank. The figure's vastness is real but unhandled.

Against that frontier, lay two measurements and feel how differently they land. Width — how many nodes the frontier can hold at once, the size of the widest antichain — is breath. A wide DAG forgives; it offers parallelism, places to put more hands. By Dilworth's theorem, that maximum width equals the minimum number of chains needed to cover the whole order, so width is literally how many independent threads of doing the figure permits at all. Depth is the opposite sensation. The longest chain — the critical path — is the incompressible spine, the sequence no help can shorten, because each link must wait on the last. Width you spend resources against; depth you only endure. You feel width in the shoulders, as room; you feel depth in the chest, as duration you do not get to negotiate.

And when you want to encircle a node — to ring "everything related" — the DAG refuses the circle and grants the cone: backward for all ancestors, forward for all descendants. Every node is the apex of two cones, narrowing to a point at the present, flaring into accomplished history behind and unspent consequence ahead. For causal reasoning this is exact — the set of nodes reachable from an event is its future light cone. To hold a node fully is to feel both at once: the weight of all that had to finish so it could begin, and the reach of all that now waits on its being done.

This is the felt core. Where it gets lived is the workbench.

5. In the hands

There is a difference between knowing a structure and having it in the body. The DAG earns the second kind of knowing through repeated collision — the build that breaks because you closed a loop, the pipeline that hangs on its critical path while parallel lanes sit idle.

In build systems, the graph is the plan. GNU Make reads your rules, builds a dependency graph, and topologically sorts it so each target is built only after its prerequisites; Kahn's algorithm repeatedly extracts a node of in-degree zero, and if none exists, a cycle has been found and Make stops — a hard error, because there is no valid order for a graph that contains one. Bazel makes it explicit: its analysis phase constructs an action graph, and incremental builds re-execute only the affected subgraph after diffing against a cached prior one. You feel it when you touch a leaf — fast, nearly instant — versus a root, where the whole transitive closure fans out.

In dataflow and scheduling, the DAG is what the system reasons over, not what you execute. Airflow defines workflows as Python DAG objects and dispatches each task once its upstream dependencies reach success. Dagster uses the same structure but makes the data assets the nodes, so the graph is a lineage map as much as a plan. Spark builds a DAG of transformations lazily — .filter(), .join(), .groupBy() execute nothing — and when an action fires, Catalyst collapses redundant stages and pushes filters toward the sources before any data moves.

In version control, history is a frozen braid. Every commit is a node; its parent pointer is an edge pointing backward in time; the graph is acyclic because a commit cannot be its own ancestor. A merge commit is a node with two parents. Rebase replays commits as new nodes on a different ancestor, rewriting object hashes because the parent hash is part of each commit's hash. git log --graph draws the commit graph with only those parent (cover) relations, the transitive edges already absent by construction.

In automatic differentiation, backprop is a reverse traversal. The forward pass builds a computation graph and caches activations; .backward() walks that graph in reverse topological order, applying the chain rule at each node. Forward for values, backward for sensitivities — one structure, two grains.

In spreadsheets, the DAG is hidden but real. Every formula cell is a node, every reference an edge; change a value and the engine recomputes downstream cells in topological order, each once, each after its inputs. A circular reference is a cycle, detected and refused — Excel raises a circular-reference warning rather than silently computing; Google Sheets shows #REF! with a circular-dependency message. The user who never heard of a DAG has been operating one for years.

In medicine, the graph stops being a plan you run and becomes an instrument for deciding what you may believe. In causal inference you draw the arrows you think are real and the diagram tells you what to hold fixed — its sharpest lesson a prohibition: a collider, the node where two arrows arrive and none leaves, is the one you must never condition on, because conditioning on it opens a path that was closed and manufactures a dependence that was never there. For decades the low-birthweight infants of smokers appeared to outlive other low-birthweight infants, until someone drew the DAG and saw the paradox was a collider held open by the very adjustment meant to make the groups comparable. A diagnostic network runs the cone backward — the clinician stands in the rain of findings and sends belief up the edges toward a hidden cause, asking not what the whole graph implies but which single untested node would most collapse the doubt. And where the body itself supplies the arrows, the acyclicity is not cut but found: a tumor's mutations stack in a partial order biology rarely lets reverse; a pathogen's phylogeny is the clearest cone in biology, ancestry widening behind and the present narrowing to a tip, until two lineages recombine and a genome arrives with two parents; and a pedigree is the one DAG you neither infer nor choose — founders at the source with nothing upstream, you a leaf whose every inherited edge was fixed before you could speak.

And in agent orchestration — the work Cruz does daily — the same structure governs swarms. Independent tasks form an antichain; fan-out dispatches the whole ready frontier, the barrier holds until a tier resolves, the pipeline chains in series. The floor on wall-clock time is the critical path, and no parallelism shortens it. The job is to keep the frontier saturated: every task with no unsatisfied dependency actively running, no agent idle while work waits.

Every habitat teaches the same lesson from a different angle. Which raises the question the calm conceals.

6. The tensions

Every lens so far describes the DAG as a thing you find — a terrain to walk, a poset to render, a cone to inhabit. None of that is wrong. But it omits the violence at the origin. A DAG is not what the world hands you; it is what remains after you cut the cycles out of it. The acyclicity that buys you a topological ordering is not a property the system possessed. It is a property you imposed, with a blade.

Look at where real DAGs come from. The build graph is acyclic because someone forbade circular imports. The git history is acyclic because rebase rewrote the braid until it lay flat. The Spark lineage is acyclic because you froze a snapshot of a computation that runs again tomorrow on new data. The Airflow DAG models a pipeline that loops daily — the loop is the real object; the DAG is one day of it, unrolled and pinned. The downhill calm those drawings radiate is the serenity of an amputated feedback loop. It reads as peace because the thing that could have argued back has been removed.

This is why the circular-dependency error deserves to be heard as more than a failure. When Make halts because Kahn's algorithm finds no node of in-degree zero, when a spreadsheet throws its circular-reference flag, the categorical refusal is not the system protecting you from a mistake. It is the system reporting that life was never acyclic in the first place. A → B → A is not a bug; it is your graph confessing that A and B were always co-determined, each waiting on the other the way revision waits on the draft that waits on the revision. Sometimes the cycle is the most honest edge in the diagram, and "break the cycle" means choose which half of a real interdependence to pretend is prior.

There is a quieter cost, and it falls hardest on exactly the work the praxis lens celebrates. To build a DAG you must assert, for every pair of nodes, either this precedes that or these are incomparable. But many real relationships are neither. Two considerations that genuinely temper each other — budget and ambition, safety and speed, the Spanish string and the English one it is not a translation of but a sibling to — have no fact of the matter about which comes first. Force them onto a DAG and you have manufactured a precedence that does not exist, then optimized a critical path through a fiction. The antichain is the form's honest gesture here — it admits some nodes simply do not know about each other — but it still demands mutual ignorance as the price of equality. It cannot hold two things deeply coupled in both directions and still co-equal. That relation has a name, and the name is cycle, and the DAG has defined it as the one thing it cannot contain.

So, the honest answer. Reach for a DAG when the irreversibility is real — when A genuinely must finish before B begins, when the arrow encodes a fact about the world and not just your scheduler. Build systems, gradient tape, commit history, causal inference: here acyclicity is discovered, and the form fits like skin. Do not reach for it when the loop is the phenomenon — negotiation, homeostasis, ecology, learning, any system where each party adjusts to the other's adjustment. Model those as DAGs and you produce a beautiful, legible, downhill-reading artifact that has quietly deleted the only dynamic that mattered. The DAG's gift is that it cannot loop. That is also, exactly and inseparably, its lie.


Insights

The acyclic graph is not discovered in the world but cut from it — every DAG is a feedback loop with the return path amputated, and its famous calm is the silence of the thing that could have argued back.

You never hold a DAG; you hold its frontier — and almost every verb worth naming (hold, orchestrate, touch, encircle) is a way of meeting an edge, never the whole, because the whole was never the unit of contact.

Width is breath and depth is fate: parallelism is the one you spend resources against, the critical path is the one you can only endure — and Dilworth's theorem is the exact exchange rate between them.

Acyclicity and the existence of a legal ordering are one fact stated from two ends; the topological sort proves only that a reading is possible, never which one is true.

The circular-dependency error is the single moment the DAG stops pretending — it is the structure confessing that two things were always co-determined, and "break the cycle" always means choosing which half to call prior.

In medicine the DAG becomes an instrument for belief, and the collider is its dark twin — the one edge you must never close, because conditioning on it manufactures a dependence that was never there: the wrong door, shut, opens a path behind you.

Open questions

  1. When you orchestrate agent swarms, which edges are discovered irreversibility and which are imposed for the scheduler's convenience? If some "dependencies" are really co-equal siblings you flattened to make the graph run — the way es/ strings are siblings to en/, not descendants — does naming them as a forced antichain, rather than a true precedence, change how you'd wire the swarm?
  1. Where in your daily work does the loop being the phenomenon get silently deleted by the DAG that models it? Your strategy work is full of feedback — pricing tempers ambition, which re-tempers pricing — yet build-DAG tooling wants a partial order. Is there a place you've been forcing a critical path through a fiction, and would a cyclic or control-loop model actually serve it better?
  1. If the only thing you ever truly hold is the frontier, what would change about how you design dashboards and orchestration views — to show not the whole graph (the affordance you can never grasp) but the moving waterline of the presently-ready, sized by width and shadowed by the critical path behind it?

← back to the doors