TensorFlow
tf.group
tf.control_dependencies
machine learning
programming

What is the difference between tf.group and tf.control_dependencies?

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

In graph-mode TensorFlow, tf.group and tf.control_dependencies are both about execution ordering, but they solve different problems. tf.group builds a single op that waits for several other ops to finish. tf.control_dependencies changes the graph-building context so later-created ops depend on earlier ones. One creates an op. The other creates dependency edges while you build new ops.

What tf.group Does

tf.group returns one op that completes only after all the input ops complete.

python
1import tensorflow as tf
2
3tf.compat.v1.disable_eager_execution()
4
5v = tf.Variable(0)
6inc1 = tf.compat.v1.assign_add(v, 1)
7inc2 = tf.compat.v1.assign_add(v, 2)
8all_done = tf.group(inc1, inc2)
9
10with tf.compat.v1.Session() as sess:
11    sess.run(tf.compat.v1.global_variables_initializer())
12    sess.run(all_done)
13    print(sess.run(v))

This is useful when you want a single op to represent "run all of these side-effecting updates."

A common example is grouping several update ops into one training step barrier.

What tf.control_dependencies Does

tf.control_dependencies does not itself run anything. Instead, it affects ops created inside its scope by attaching control edges to them.

python
1import tensorflow as tf
2
3tf.compat.v1.disable_eager_execution()
4
5v = tf.Variable(0)
6inc = tf.compat.v1.assign_add(v, 1)
7
8with tf.control_dependencies([inc]):
9    read_after_inc = tf.identity(v)
10
11with tf.compat.v1.Session() as sess:
12    sess.run(tf.compat.v1.global_variables_initializer())
13    print(sess.run(read_after_inc))

Here, read_after_inc depends on inc, so TensorFlow will not evaluate the identity before the increment happens.

The Core Difference

The simplest way to remember it is:

  • 'tf.group creates one grouped op out of existing ops'
  • 'tf.control_dependencies influences newly created ops so they depend on existing ops'

So if you already have several ops and want one handle that means "all of them," use tf.group.

If you are building a later op and need to force it to happen after some earlier op, use tf.control_dependencies.

A Practical Comparison

Suppose you have two variable updates and a later read.

With tf.group:

python
updates = tf.group(update_a, update_b)
with tf.control_dependencies([updates]):
    final_value = tf.identity(v)

This says:

  • group both updates into one op
  • make the read happen after that grouped op

That shows how the two constructs can be combined rather than treated as rivals.

Why Graph Mode Needed These Tools

In graph mode, TensorFlow schedules operations based on dataflow and explicit control edges. If two ops do not have a data dependency and no control dependency is added, TensorFlow is free to execute them in any order that satisfies the graph.

That is why control constructs mattered so much for:

  • update operations
  • variable assignments
  • initialization ordering
  • summary and bookkeeping side effects

In eager execution, these issues are less visible because operations run immediately, but older graph code still uses these APIs heavily.

When tf.group Is the Better Fit

Use tf.group when you want:

  • one op representing several updates
  • a clean handle for "finish all of these"
  • a simple barrier op to feed into later dependencies

It is especially handy when many update ops are already constructed.

When tf.control_dependencies Is the Better Fit

Use tf.control_dependencies when:

  • you are about to create new ops that must run after existing ones
  • you need explicit ordering for a later read or computation
  • you want the dependency attached during graph construction

It is more about how future graph nodes are built than about packaging old nodes together.

Common Pitfalls

  • Treating tf.control_dependencies as if it executes the dependency ops by itself. It only adds control edges to later-created ops.
  • Using tf.group when what you actually need is to force a later op to wait during graph construction.
  • Forgetting that these APIs matter mainly in graph mode, not in ordinary eager execution.
  • Building side-effecting ops without any data or control dependency and assuming TensorFlow will run them in the desired order automatically.
  • Assuming the two APIs are interchangeable when one creates an op and the other changes the dependency structure of future ops.

Summary

  • 'tf.group returns a single op that completes after several input ops finish.'
  • 'tf.control_dependencies adds dependency edges to ops created inside its scope.'
  • One is for grouping existing ops; the other is for constraining later-created ops.
  • They are often complementary rather than mutually exclusive.
  • In graph-mode TensorFlow, both exist to make execution order explicit when data dependencies alone are not enough.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.