Designing Maintainable Graphs
Explore how to design maintainable LangGraph workflows by ensuring each node has a single responsibility, using clear naming conventions, and managing state ownership. Understand when to refactor complex graphs into subgraphs, apply disciplined state management, and avoid over-engineering to build reliable, scalable AI systems with transparent logic and easy debugging.
We'll cover the following...
- One job per node
- Naming nodes and state fields clearly
- State discipline: field ownership
- Recognising when a graph needs to be split
- The over-engineering trap
- What we are building
- State schemas before and after
- Build it step by step
- Complete executable code
- A pre-project checklist
- Exercise
- Solution
- Terms introduced in this lesson
A graph workflow starts clean. There are three nodes, four edges, and the diagram fits on half a screen. Then a new requirement arrives. A fourth node is added. Then a fifth. A routing function that started with two branches grows to five. A state schema that had six fields quietly accumulates twelve. The diagram that fit on half a screen now requires scrolling in every direction.
A poorly structured graph is no more readable than a poorly structured function. The abstraction changes, nodes and edges instead of lines and branches, but the underlying problem is the same: too much responsibility in too few places, too many implicit assumptions between parts, and names that made sense to the person who wrote them three weeks ago but mean nothing to everyone else.
The patterns in this lesson address that problem before it starts. They apply to any workflow, at any size, and they are easiest to enforce when we think about them at the design stage rather than after the graph is already tangled.
One job per node
The most important rule in graph design is that each node should do exactly one thing. Not “one category of things.” One thing.
A node that is doing more than one thing usually shows one of three signals. It reads from fields belonging to multiple distinct phases of the workflow. It writes to more fields than the number of things a caller would naturally describe as its responsibility. Or its function name requires an “and” to describe it: classify_and_draft, validate_and_format, search_and_summarise. The table below lists common examples of overgrown nodes alongside how they should be split.
Overgrown node | What it actually does | Clean split |
| Classifies, drafts a response, and checks quality |
|
| Calls a tool and then generates a synthesis from the result |
|
| Validates model output and decides which handler to run |
|
| Constructs a long prompt and calls the model |
|
Splitting these is not just an aesthetic choice. It produces two practical benefits. First, when something goes wrong, the state snapshot after each node immediately shows which step failed and what value it wrote. Second, when a requirement changes, the quality threshold needs to move, or the draft prompt needs a new instruction, we change one node and nothing else needs to touch it.
Naming nodes and state fields clearly
Node and field names are the first thing anyone reads when looking at a graph. They carry more information per character than any comment.
For nodes, the most readable convention is a verb followed by a noun:
classify_intent,draft_response,validate_output,route_by_confidence. The verb describes what the node does; the noun describes what it acts on. Avoid numbering (step_1,step_2) and avoid generic terms (process,handle,run).For state fields, names should describe the content, not the phase.
draft_responseis clear.outputis not, every node produces output.user_queryis clear.qis ...