Chains and Composition

A "chain" is just Runnables composed with LCEL — but the word names the central idea of LangChain: build applications by wiring small, standard components into pipelines rather than writing monolithic prompt-and-parse code. Thinking in chains is thinking in composable steps, which is what makes LangChain applications modular, testable, and maintainable.

The last post covered LCEL and Runnables — the mechanism. This post covers what you build with them: chains. A chain is a composition of components into an application pipeline, and “chaining” is the concept LangChain is named for. This post looks at composition patterns — sequential steps, parallel branches, routing, passing data through — and the design mindset of building LLM applications as composed steps. It’s about turning the mechanism into real application structure.

What a chain is

A chain is simply a sequence (or graph) of components composed together so data flows through them — built with LCEL from Runnables (the last post). The simplest chain is prompt | model | parser; a real application chain adds more steps: retrieve context, format it into a prompt, call the model, parse the output, maybe post-process. The essence is the same: an application built as a pipeline of composable steps rather than one big function that does everything.

This chaining mindset is LangChain’s core contribution to how you structure LLM code. Instead of a monolithic block that builds a prompt, calls the model, and parses the result all tangled together, you compose named, standard components — each doing one thing — into a pipeline. The benefits are the usual benefits of good decomposition, applied to LLM apps:

Thinking in chains is thinking in composable steps, and that’s the shift that makes LangChain applications maintainable rather than a pile of prompt-wrangling code.

The RAG chain: the canonical example

The most illustrative chain is retrieval-augmented generation (the Agentic RAG series covers RAG deeply; here it’s the exemplar chain). A RAG chain composes retrieval and generation:

question
   │
   ├─(parallel)→ retriever → relevant documents ┐
   │                                            ├→ prompt (context + question) → model → parser → answer
   └──────────── pass through the question ─────┘
# Illustrative shape — see the LangChain docs for exact API.
rag_chain = (
    {"context": retriever, "question": RunnablePassthrough()}   # parallel: retrieve AND forward
    | prompt                                                    # format context + question
    | model
    | output_parser
)
answer = rag_chain.invoke("What is our refund policy?")

This one example shows several composition patterns at once:

The RAG chain is the canonical LangChain composition because it exercises the real patterns — parallel, sequential, passthrough — in a genuinely useful application, and it shows how a substantial capability (retrieval-grounded answering) is composed from standard components rather than hand-built. Most LangChain applications are variations and extensions of this shape.

Composition patterns

Beyond the linear pipe, a few patterns recur in building real chains (all from the Runnables/LCEL toolkit):

These patterns are the vocabulary of chain-building. Real applications combine them: a chain that routes by input type, retrieves in parallel where relevant, runs sub-chains, and shapes data between steps — all declaratively composed. The skill is decomposing your application into these composable steps.

When chains are the right shape (and when they aren’t)

Chains (LCEL composition) are LangChain’s sweet spot, but they have a boundary worth respecting, reiterating the LangChain-vs-LangGraph line:

So chains are the right tool for the large class of pipeline-shaped LLM applications, and recognizing when your application has outgrown that shape — into genuine statefulness and cycles — is when you graduate to LangGraph. Using chains for what they’re good at, and not forcing cyclic orchestration into them, is the practical wisdom.

The composition mindset

The deeper takeaway of this post is a way of thinking: build LLM applications as compositions of small, standard, testable components rather than monolithic code. Chains make this concrete — you decompose the application into steps (retrieve, format, generate, parse, route), implement or reuse each as a Runnable, and compose them with LCEL. This is ordinary good software design (decomposition, reuse, testability) applied to LLM applications, and it’s LangChain’s most durable contribution regardless of the specific API. The next posts add more components to compose — retrieval, tools, memory — but the mindset stays: everything is a component, and you build by composing them into chains.

Key takeaways

Further reading

Sources & References