When you describe a workflow in plain prose, the model returns a linear script that handles the happy path and nothing else. The failure only surfaces weeks later, when a webhook fires twice, an approval arrives out of order, or a job gets cancelled mid run. The code was never a workflow. It was a sequence of steps dressed up as one.
The Details:
Prose descriptions are inherently linear, so generated code becomes a chain of status checks that assumes events arrive in order. A real workflow is a graph: an entity sits in a defined state, and only certain events may move it to another. Without that constraint, a database column will happily accept an illegal write, like approving a request that was already denied.
Bolted on booleans are the tell. Adding is_cancelled to a job that already has pending, running, done, and failed creates a state nobody named: running and cancelled at once. Every future piece of code touching that record now has to remember to check two fields instead of one, and eventually someone forgets.
The fix is a transition matrix: every state on both axes, a deliberate yes or no in every cell. This forces decisions prose skips, like whether a completion event can arrive before a start event. Empty, unconsidered cells are exactly where these bugs hide.
The prompt itself has to change. Asking for a robust workflow yields an adjective. Asking for an enum of valid states, a transition function that rejects disallowed pairs, and tests covering every illegal transition yields a verifiable specification, and a model that writes to it produces a state machine instead of a script.
Bottom Line: The state matrix is the design; the generated code is just the receipt, so hand the model states and transitions, not a story about what happens next.
Enjoy this article?
Listen to the Claude Code Conversations radio show or join the community.