--- slug: composition type: pattern summary: "Combining smaller, simpler parts into something larger and more capable, building small things that snap together rather than one big thing." created: 2026-04-04 updated: 2026-04-05 related: abstraction: relation: depends-on note: "Composition works best when parts hide their internals." component: relation: produces note: "A composition of parts often becomes a new component." contract: relation: uses note: "Parts compose through compatible interfaces." decomposition: relation: related note: "Decomposition breaks things apart; composition puts them together." dependency: relation: requires note: "Management — composed parts depend on each other." interface: relation: uses note: "Parts compose through compatible interfaces." monolith: relation: contrasts-with note: "A monolith resists composition by entangling concerns." task-decomposition: relation: supported-by note: "Well-decomposed tasks compose into completed features." --- # Composition > **Pattern** > > A named solution to a recurring problem. > "Favor composition over inheritance." > — Gang of Four, *Design Patterns* ## Understand This First - [Abstraction](abstraction.md) -- composition works best when parts hide their internals. ## Context Systems are built from parts. **Composition** is the act of combining smaller, simpler parts into something larger and more capable. It operates at the **architectural** scale. Instead of building one big thing, you build small things that snap together. Composition appears everywhere: functions calling functions, [components](component.md) wiring together, services coordinating through APIs, and AI agent workflows chaining tool calls into multi-step plans. Wherever small pieces combine to produce behavior that none could produce alone, composition is at work. ## Problem How do you build complex behavior without creating complex parts? ## Forces - Complex requirements demand complex results, but complex implementations are hard to understand and maintain. - Building everything from scratch is wasteful. Many problems have already been solved. - Combining parts requires compatible [interfaces](interface.md). Parts that can't communicate can't compose. - Deeply nested compositions can become hard to follow, even if each piece is simple. ## Solution Build small, focused parts that each do one thing well. Give each part a clear [interface](interface.md). Then combine them to produce the behavior you need. The combination itself should be simple: ideally, just wiring outputs to inputs. Effective composition requires parts that are: - **Self-contained** — each part works without knowing how it will be combined. - **Composable** — parts accept standard inputs and produce standard outputs. - **Substitutable** — you can swap one part for another that has the same interface. Unix pipes are a classic example: `cat file.txt | grep "error" | sort | uniq -c`. Each tool does one thing. The pipe operator composes them into something none of them could do alone. In agentic coding, composition is how agents accomplish complex tasks. An agent doesn't solve a big problem in one step. It decomposes the goal into sub-tasks, uses tools to complete each one, and composes the results. The quality of the available tools (their clarity, their [contracts](contract.md), their composability) directly determines how effectively the agent can work. ## How It Plays Out A data processing system needs to ingest CSV files, validate records, enrich them with data from an API, and write the results to a database. Instead of building one monolithic script, the team builds four stages: `parse`, `validate`, `enrich`, and `store`. Each stage reads from a queue and writes to the next. When the enrichment API changes, only the `enrich` stage changes. When a new output format is needed, a new `store` stage is added alongside the existing one. An AI agent is asked to prepare a code review. It composes several tool calls: first `search_code` to find the changed files, then `read_file` on each one, then `run_tests` to check for regressions, then it synthesizes a review. Each tool is simple. The agent's plan — the composition — is where the intelligence lives. If the tools are well-designed and composable, the agent's plan works. If they produce inconsistent formats or have surprising side effects, the composition falls apart. > **💡 Example Prompt** > > "Build the data pipeline as four composable stages: parse, validate, enrich, and store. Each stage should read from an input queue and write to the next. I want to be able to replace or add stages without rewriting the others." ## Consequences Composition keeps individual parts simple while enabling complex outcomes. It supports reuse: the same parts can appear in different compositions. It supports evolution: you can replace or add parts without rewriting the whole. The cost is coordination. Composed parts must agree on data formats, error handling, and sequencing. When a composed system fails, debugging can be harder because the bug might be in any part or in the wiring between them. Good logging, clear [contracts](contract.md), and predictable error propagation are essential complements to compositional design. --- - [Next: Separation of Concerns](separation-of-concerns.md) - [Previous: Dependency](dependency.md)