Skip to content
Ian Cunningham monogram Ian Cunningham AI systems builder

Blog

Command vs Send in LangGraph: Choosing the Right Primitive

When to use Command for control flow and Send for data parallelism in LangGraph, with practical testing examples for each.

Command vs Send in LangGraph: Choosing the Right Primitive
KT

Article summary

Key Takeaways

  1. Send and Command solve fundamentally different problems

    Send distributes the same work across multiple inputs, while Command controls which actions happen next. Treating them as interchangeable leads to confusion.

  2. Choosing the wrong primitive makes systems harder to reason about

    Misusing Send or Command doesn’t break execution, but it introduces unnecessary complexity, awkward state, and harder-to-test workflows.

  3. Clear intent leads to maintainable systems

    Using Send for data parallelism and Command for orchestration keeps workflows predictable, testable, and easier to evolve over time.

This article is part of the 7-part Testing LangGraph Applications series. The examples come from the langgraph-testing-demo repository.

Testing LangGraph Applications Series

  1. Stop Testing AI Outputs. Start Testing State
  2. How to Structure LangGraph Tests That Actually Scale
  3. Testing Isn’t Enough: Evaluating LangGraph Workflows That Actually Work
  4. Testing Parallel LangGraph Workflows Without Losing Control
  5. Understanding LangGraph Workflows with LangSmith Traces and pytest
  6. Command vs Send in LangGraph: Choosing the Right Primitive ← You are here
  7. What It Takes to Build Production-Ready LangGraph Systems

All examples in this article are backed by a pytest suite covering both Command-based control flow and Send-based parallel execution patterns:

Pytest results

When you first start using LangGraph, Send and Command can feel interchangeable.

They’re not.

You can use both to trigger multiple nodes. You can use both for parallel execution. And if you’re not careful, you’ll end up picking one arbitrarily.

That’s where things start to break down.

Choosing the wrong primitive doesn’t just affect execution. It affects how understandable, testable, and maintainable your graph becomes.

The Core Difference

The easiest way to think about this is:

Send distributes work. Command controls flow.

They solve different problems.

What Send Is For

Send is about data parallelism.

You take:

  • one node
  • multiple inputs

…and run the same logic multiple times.

Example:

research_tasks → Send → researcher (N times)

This is exactly what we implemented in the parallel demo:

  • planner creates multiple research tasks
  • each task is processed by the same researcher node
  • results are aggregated

This is a classic map-style pattern.

When Send is the right choice

Use Send when:

  • You are processing a list of items
  • The same logic applies to each item
  • The number of tasks may vary
  • You need to aggregate results afterward

Typical use cases:

  • research pipelines
  • retrieval + chunk processing
  • scoring / ranking multiple items

What Command Is For

Command is about control flow.

It allows a node to decide:

  • what happens next
  • which nodes should run
  • what state updates should accompany that decision

Example:

intent → Command → send_email / send_slack / send_tweet

Here, you’re not repeating the same work.

You’re orchestrating different actions.

When Command is the right choice

Use Command when:

  • You are routing between different nodes
  • Each node represents a different behavior
  • You want explicit orchestration logic
  • You may trigger multiple actions in parallel

Typical use cases:

  • notification systems (email, Slack, etc.)
  • tool selection
  • multi-agent orchestration
  • user intent routing

A Subtle but Important Point

You might have seen guidance like:

“Command is the more modern primitive for parallel execution”

That’s only partially true.

A more precise way to say it is:

Command is the more general primitive. Send is the more specialized one.

  • Send is optimized for fan-out of the same node
  • Command is designed for orchestration across different nodes

Both can produce parallel execution.

But they represent different intent.

Real Example: Send (From This Repo)

In the parallel demo:

  • planner generates research topics
  • each topic is processed independently
  • all results are merged

Conceptually:

["technical", "failures", "testing"]

Send → researcher

aggregation

This is clean because:

  • the node is the same
  • the structure is predictable
  • testing focuses on aggregation and counts

Real Example: Command (New Pattern)

Now consider this:

user intent → notify system

The system decides:

  • send Slack message
  • send email
  • maybe do both

That node can return:

from langgraph.types import Command

return Command(
    goto=["send_slack", "send_email"],
    update={"notification_sent": True},
)

This is fundamentally different from Send.

  • Each node does different work
  • There is no aggregation step
  • The goal is orchestration, not distribution

Why This Distinction Matters

You can misuse these primitives.

And when you do, the graph still runs, but it becomes harder to reason about.

If you misuse Send

Using Send for orchestration leads to:

  • unclear intent
  • awkward state structures
  • unnecessary aggregation logic

You’re forcing a data-parallel pattern onto a control-flow problem.

If you misuse Command

Using Command for data fan-out leads to:

  • over-complicated routing logic
  • unnecessary branching
  • harder-to-test graphs

You’re turning a simple map problem into orchestration.

Testing Implications

This ties directly back to earlier posts.

Testing Send

Focus on:

  • number of outputs
  • aggregation correctness
  • handling partial failures

Example:

assert len(result["research_results"]) == 3

Testing Command

Focus on:

  • correct routing decisions
  • correct nodes executed
  • absence of unintended paths

Example:

assert "slack_sent" in result
assert "email_sent" in result

Hybrid Patterns

In real systems, you often combine both.

Example:

Command → choose research strategy

Send → execute multiple research tasks

Here:

  • Command decides what to do
  • Send decides how to scale it

This is a powerful pattern.

Practical Rules

If you’re unsure which to use, ask:

Are you distributing work?

  • Same node
  • Multiple inputs
  • Aggregation required

👉 Use Send

Are you orchestrating behavior?

  • Different nodes
  • Explicit decisions
  • Multiple actions

👉 Use Command

The Real Takeaway

You can use both Send and Command to trigger multiple nodes.

But they represent different mental models:

  • Send scales work
  • Command controls flow

Confuse them, and your graph becomes harder to understand.

Use them correctly, and your system stays clean as it grows.

What’s Next

At this point, we’ve covered:

  • state design
  • testing
  • evaluation
  • parallelism
  • observability
  • orchestration

The final step is to bring this together:

What does a production-ready LangGraph system actually look like?

That’s what we’ll cover in the final article.

Final Thought

LangGraph gives you powerful primitives.

The challenge isn’t just using them.

It’s choosing the right one for the job.

That’s what keeps your system understandable six months later, not just working today.

Work with Ian

Need a workflow, pipeline, or copilot built for a real operational use case?

If this post aligns with what you are building, I can help scope the implementation and turn the concept into a production-ready system.