State
“The hardest bugs are the ones that depend on what happened before.” — Common engineering wisdom
Understand This First
- Data Structure – data structures are the containers that hold state in memory.
Context
A program that only computes outputs from inputs, with no memory of what happened before, is simple to reason about. But most useful software remembers things: the items in your shopping cart, the current step in a workflow, whether you’re logged in. That remembered information is state. Managing state is an architectural concern because it affects everything from how you test code to how you scale a system to how an AI agent reasons about your program.
Problem
How do you keep track of the information a system needs to remember between operations, without that remembered information becoming a source of confusion and bugs?
State is the reason programs behave differently when you run them a second time. It’s why “it works on my machine” is a meme. Every piece of state is something that can be in an unexpected condition: stale, corrupted, out of sync with another piece of state. The more state a system carries, the more ways it can go wrong.
Forces
- Users expect systems to remember things (their preferences, their progress, their data).
- More state means more possible configurations, which means more potential bugs.
- State that is spread across many places is hard to understand and hard to keep consistent.
- Stateless components are easier to test, scale, and replace, but pure statelessness is rarely practical for a whole system.
Solution
Be deliberate about state. For every piece of information your system remembers, decide three things: where it lives (which component owns it), how long it lasts (request-scoped, session-scoped, persistent), and who can change it (which code paths are allowed to write).
Minimize state where possible. If a value can be computed from other values, compute it rather than storing it separately. This is the DRY principle applied to state. When state is necessary, concentrate it. A single Source of Truth for each piece of information is far easier to manage than the same information scattered across three services and a browser cookie.
Isolate state from logic. Functions that take inputs and produce outputs without reading or writing external state are easy to test, easy to reuse, and easy for an AI agent to generate correctly. Push state to the edges — read it at the start, pass it through pure logic, write the result at the end.
How It Plays Out
A web application stores the user’s shopping cart in three places: the browser’s local storage, a session on the server, and a row in the database. When the user adds an item from their phone, only the database updates. The browser still shows the old cart. Two pieces of state have diverged, and the user sees inconsistent data. The fix is to designate the database as the Source of Truth and treat everything else as a cache that refreshes from it.
When an AI agent generates a function that modifies global state (updating a counter, appending to a log, changing a configuration), bugs become hard to reproduce because the function’s behavior depends on what happened before. Instructing the agent to write pure functions that accept state as input and return new state as output produces code that’s testable and predictable.
AI agents are particularly prone to creating hidden state: module-level variables, singletons, mutable globals. When reviewing agent-generated code, search for state that’s modified outside the function that owns it.
“Refactor this function so it doesn’t modify the global config object. Instead, accept the config values it needs as parameters and return the new state as output.”
Consequences
Deliberate state management makes systems predictable, testable, and debuggable. When you know where every piece of state lives and who can change it, you can reason about behavior without running the whole system in your head.
The cost is discipline. Minimizing state sometimes means more parameters being passed around. Centralizing state sometimes means more network calls. Some domains are inherently stateful — a multiplayer game, a collaborative editor, a trading system — where you can’t avoid managing complex, rapidly changing state. In those cases, patterns like Transactions and Atomic operations become essential.
Related Patterns
- Uses / Depends on: Data Structure — data structures are the containers that hold state in memory.
- Enables: Source of Truth — deciding where state lives leads to designating a source of truth.
- Enables: Consistency — state management is a prerequisite for maintaining consistency.
- Refined by: Transaction — transactions provide controlled ways to modify state safely.
- Refined by: Atomic — atomic operations prevent state from being observed in a half-updated condition.
- Contrasts with: DRY — DRY reduces state by deriving values instead of storing them separately.