# How Jenkins Implements Dependency Graph Tracking with DependencyGraph for Build Triggers

> Discover how Jenkins uses DependencyGraph to track build dependencies. Learn about its immutable graph and how components declare dependencies for efficient build triggers.

- Repository: [Jenkins/jenkins](https://github.com/jenkinsci/jenkins)
- Tags: internals
- Published: 2026-07-28

---

**Jenkins maintains a bi-directional, immutable dependency graph via `hudson.model.DependencyGraph` that rebuilds when configurations change, with edges contributed by components implementing the `DependencyDeclarer` interface.**

The jenkinsci/jenkins repository implements sophisticated dependency graph tracking to orchestrate complex build pipelines and trigger downstream jobs automatically. At the center of this mechanism is the `DependencyGraph` class, which stores relationships between `AbstractProject` instances and determines when downstream builds should fire based on upstream completion events.

## Core Architecture of the DependencyGraph

### Graph Construction and Immutability

When Jenkins starts or when `Jenkins.rebuildDependencyGraph()` is called, the system creates a fresh `DependencyGraph` instance. Inside `DependencyGraph.build()` (lines 85‑92 of [`core/src/main/java/hudson/model/DependencyGraph.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/hudson/model/DependencyGraph.java)), the method enumerates all items of type `AbstractProject` and asks each project to contribute its edges:

```java
for (AbstractProject p : Jenkins.get().allItems(AbstractProject.class))
    p.buildDependencyGraph(this);

```

The `AbstractProject.buildDependencyGraph(DependencyGraph)` method delegates to the project's **publishers**, **builders**, **wrappers**, and **triggers**. Each component can implement `jenkins.model.DependencyDeclarer` to hook into this process and register dependencies.

### Bidirectional Edge Storage

The graph stores relationships in two concurrent hash maps: `forward` (upstream to downstream) and `backward` (downstream to upstream). When a component calls `graph.addDependency(Dependency)`, the method populates both maps simultaneously to enable efficient traversal in either direction:

```java
public void addDependency(Dependency dep) {
    if (built) throw new IllegalStateException();
    add(forward, dep.getUpstreamProject(), dep);
    add(backward, dep.getDownstreamProject(), dep);
}

```

The `Dependency` object records the upstream and downstream projects and defines **`shouldTriggerBuild`**, which determines whether a downstream build should start when the upstream finishes.

## How Build Triggers Register Dependencies

### The DependencyDeclarer Interface

Plugins and core components declare dependencies by implementing `jenkins.model.DependencyDeclarer`. This interface provides a single method, `buildDependencyGraph(AbstractProject owner, DependencyGraph graph)`, which Jenkins calls during graph reconstruction. Implementations add edges by calling `graph.addDependency()` with a `Dependency` subclass that encapsulates custom trigger logic.

### ReverseBuildTrigger Implementation

`ReverseBuildTrigger` in [`core/src/main/java/jenkins/triggers/ReverseBuildTrigger.java`](https://github.com/jenkinsci/jenkins/blob/main/core/src/main/java/jenkins/triggers/ReverseBuildTrigger.java) (lines 72‑79) demonstrates how a trigger living on the downstream job registers a dependency on upstream completion:

```java
@Override public void buildDependencyGraph(final AbstractProject downstream, DependencyGraph graph) {
    for (AbstractProject upstream :
         Items.fromNameList(downstream.getParent(), getUpstreamProjects(), AbstractProject.class)) {
        graph.addDependency(new DependencyGraph.Dependency(upstream, downstream) {
            @Override public boolean shouldTriggerBuild(AbstractBuild upstreamBuild,
                                                          TaskListener listener, List<Action> actions) {
                return shouldTrigger(upstreamBuild, listener);
            }
        });
    }
}

```

This trigger creates an anonymous `Dependency` subclass that overrides `shouldTriggerBuild` to check the upstream build result against configured thresholds.

### Trigger Execution Flow

When an upstream build completes, the queue runner invokes `Dependency.Dependency.shouldTriggerBuild`. For `ReverseBuildTrigger`, the actual scheduling happens in `ReverseBuildTrigger.RunListenerImpl.onCompleted` (lines 72‑99):

```java
if (trigger.shouldTrigger(r, listener)) {
    ParameterizedJobMixIn.scheduleBuild2(trigger.job, -1,
        new CauseAction(new Cause.UpstreamCause(r)));
}

```

The listener retrieves cached triggers for the completed build’s project, evaluates `shouldTrigger`, and schedules the downstream job via `ParameterizedJobMixIn.scheduleBuild2` with an `UpstreamCause`.

## Topological Ordering and Cycle Detection

After all projects contribute edges, `DependencyGraph.build()` finalizes the forward and backward maps, wraps them in unmodifiable collections, and executes Tarjan’s strongly connected components algorithm (lines 101‑135). This computes a stable `topologicalOrder` used for project comparison via `DependencyGraph.compare()` and for detecting circular dependencies in the job hierarchy.

## Graph Rebuild Lifecycle

### When and How the Graph Rebuilds

Reconstruction occurs in two scenarios:

- **Manual request** – Calling `Jenkins.get().rebuildDependencyGraph()` (or the asynchronous `rebuildDependencyGraphAsync()`)
- **Configuration change** – Most configurables invoke `rebuildDependencyGraphAsync()` after persisting changes to disk

Because the graph is immutable after construction, Jenkins atomically replaces the old instance with a new one without requiring synchronization locks, making the rebuild process thread-safe and cheap.

## Querying Dependencies at Runtime

Developers can access the current graph to inspect relationships programmatically:

```java
// Force a rebuild (e.g., from a system script)
Jenkins.get().rebuildDependencyGraph();   // triggers a fresh DependencyGraph build

// Implement a simple DependencyDeclarer (e.g., a custom publisher)
public class MyPublisher extends Recorder implements DependencyDeclarer {
    @Override
    public void buildDependencyGraph(AbstractProject owner, DependencyGraph graph) {
        // add an edge from a static upstream job to the current job
        Job<?,?> upstream = Jenkins.get().getItem("my-upstream", Job.class);
        if (upstream != null) {
            graph.addDependency(new DependencyGraph.Dependency(upstream, owner));
        }
    }
}

// Query the graph at runtime
DependencyGraph dg = Jenkins.get().getDependencyGraph();
List<AbstractProject> downstream = dg.getDownstream(someProject);
System.out.println("Downstream jobs: " + downstream);

```

## Summary

- Jenkins uses an immutable `DependencyGraph` stored in `hudson.model.DependencyGraph` to track bi-directional job relationships
- The `DependencyDeclarer` interface in `jenkins.model` allows publishers, builders, and triggers to contribute edges during graph construction
- `ReverseBuildTrigger` exemplifies downstream trigger logic by implementing custom `shouldTriggerBuild` logic in [`jenkins/triggers/ReverseBuildTrigger.java`](https://github.com/jenkinsci/jenkins/blob/main/jenkins/triggers/ReverseBuildTrigger.java)
- Graph reconstruction happens atomically via `Jenkins.rebuildDependencyGraph()` or asynchronously after configuration changes
- Tarjan’s SCC algorithm provides topological sorting and cycle detection across the project hierarchy

## Frequently Asked Questions

### How does Jenkins detect circular dependencies between jobs?

The `DependencyGraph` class performs a topological sort using Tarjan’s strongly connected components algorithm during the `build()` method. Strongly connected components indicate cycles, which Jenkins handles by computing a stable `topologicalOrder` that enforces a consistent project ranking and prevents infinite trigger loops.

### Can plugins declare custom dependencies without modifying core Jenkins code?

Yes. Plugins implement the `jenkins.model.DependencyDeclarer` interface on their extensions (such as publishers or triggers). During graph reconstruction, Jenkins automatically invokes `buildDependencyGraph()` on these components, allowing them to call `graph.addDependency()` with custom `Dependency` objects that define specific trigger conditions via `shouldTriggerBuild()`.

### What happens to running builds when the dependency graph is rebuilt?

Running builds remain unaffected because `DependencyGraph` is immutable after construction. When `rebuildDependencyGraph()` executes, it creates a completely new instance and atomically swaps the reference in the Jenkins singleton. Existing builds continue referencing the graph instance that existed at their start, ensuring thread safety without requiring synchronization locks.

### How does ReverseBuildTrigger determine whether to fire a downstream build?

The trigger implements a custom `shouldTriggerBuild()` method that evaluates the upstream build result (e.g., checking if it matches `Result.SUCCESS` or other configured thresholds) and verifies user permissions. The `RunListenerImpl` invokes this check upon upstream completion in `onCompleted()`, and only calls `ParameterizedJobMixIn.scheduleBuild2()` if the method returns true.