Skip to course content
Free LangGraph course

LangGraph for Agentic Workflows

Unit 02.01: A node does one job to the state

The single most useful discipline in graph design is keeping each node to one job. It costs a few extra nodes and saves every debugging session afterwards.

One job, one testable function

A node that counts words is a function you can test with a string. A node that trims, counts and flags is three behaviours behind one name, and when the flag is wrong you have three candidates.

The code shows both, and tests the single-job versions with plain dictionaries.

from typing import TypedDict


class State(TypedDict):
    text: str
    words: int
    flagged: bool


# One job each. Each is testable on its own, with no graph involved.
def count(state: State):
    return {"words": len(state["text"].split())}


def flag(state: State):
    return {"flagged": state["words"] > 5}


# A node that does three jobs. Which line failed when `flagged` is wrong?
def count_and_flag_and_trim(state: State):
    text = state["text"].strip()
    words = len(text.split())
    return {"text": text, "words": words, "flagged": words > 5}


s = {"text": "  a graph node should do one job  ", "words": 0, "flagged": False}
print("count:", count(s))
print("flag :", flag({**s, "words": 7}))
print("\ntesting a single-job node needs no graph, no mocks, no checkpointer.")

count and flag are each three lines and need no graph, no mocks and no checkpointer to test. count_and_flag_and_trim needs all three inputs set up correctly to test any one of its behaviours.

The graph does get longer. That is the trade: more nodes, each trivially verifiable, against fewer nodes that each need reasoning about. In a system that will be debugged from a trace, more nodes is the better side of it - the trace names the node, so a narrow node names the bug.

The mistake this prevents

The mistake is merging nodes to reduce the diagram's size. The diagram is read once when someone joins the project; the trace is read every time something breaks. Optimise for the trace.

Takeaway

One job per node. The node name should describe exactly what changed in the state, and a test should be a dictionary in and a dictionary out.