Hidden State Coupling
also known as Invisible Workflow Coupling, Undeclared Shared State
Anti-pattern: agent workflows read or write undeclared shared state (caches, env vars, process globals) instead of explicit inputs and outputs.
Context
Multiple agent workflows or steps interact with the same underlying state — a process-global cache, an env-var-configured singleton, an external store — but the dependency is implicit. Nothing in the workflow signature names the shared state.
Problem
When the shared state mutates in unexpected ways, dependent workflows experience silent retry storms, duplicated side effects, or behavior changes nobody can trace. Postmortems are slow because the coupling is invisible to readers of the agent code. Reproduction in test environments often fails because tests bypass the shared singleton.
Forces
- Globals and caches are convenient and reduce verbose plumbing.
- Making every input explicit looks like over-engineering at small scale.
- Hidden coupling rarely fails in dev where there is one process and one user.
Example
A research agent reads from a process-global LRU cache populated by a separate ingestion workflow. The ingestion workflow restarts and the cache empties. The research agent's behavior changes silently — answers regress because the cache miss path produces different rankings. The agent code never mentions the cache. Postmortem takes 3 days to localize.
Diagram
Solution
Therefore:
Pass all inputs as arguments to the workflow function. Where shared state is genuinely needed (caches, feature flags), route it through a typed accessor with version stamping and structured logging. Treat the agent run as a pure-ish function of its declared inputs so replay produces the same result. Pair with stateless-reducer-agent and provenance-ledger to make every state read auditable.
What this pattern forbids. No useful constraint; the missing constraint is explicit-input discipline at the workflow boundary.
And the patterns that stand alongside it, or against it —
- complementsStateless Reducer Agent★— Design the agent as a pure function (state, event) → newState; entire execution history is held in an external event log; enables pause / resume / replay / time-travel without bespoke checkpointing.
- complementsProvenance Ledger★★— Log every agent decision and state change with enough metadata to explain or reverse it later.
- complementsMissing Idempotency on Agent Calls✕— Anti-pattern: retry state-mutating agent tool calls without idempotency keys, so retries multiply real-world side effects.
- complementsRace Conditions on Shared Tool Resources✕— Anti-pattern: let concurrent agents perform read-modify-write on shared external resources without locking, producing silent data corruption.
- complementsErrors Swept Under the Rug✕— Anti-pattern: scrub failed actions, stack traces, and error observations from the agent's own context so the trace looks clean, leaving the model with no evidence of what did not work.
Neighbourhood
Click any neighbour to follow the language. Scroll to zoom, drag to pan.