A working hypothesis from repeated coding agents use: FP-style structure seems to make non-FP codebases easier for coding agents to understand, change, and verify.

The Missing Variable In Agentic Coding
Picture a Monday-morning task in a Python or TypeScript service. The request looks ordinary: add a validation rule, thread one more field through a pipeline, keep the old API stable, update the tests, and do not break the nightly job. You hand the task to a coding agent. In one repository the run feels clumsy. The agent reads too much, edits the wrong layer first, and produces a patch that looks plausible until you notice the missed edge case. In another repository the same class of task goes much better. The agent locks onto the relevant types, updates a few focused functions, adjusts the tests, and lands in the right area with much less wandering.
I have been seeing that difference often enough with Codex, which is my primary coding agent, that I no longer think of it as luck. The usual discussion around agentic coding centers on model quality, tool access, prompts, harnesses, and review loops. All of that matters. A more basic variable often gets less attention: what kind of code structure gives an agent a fair chance to work well?
This essay is a continuation of two earlier posts: Functional Programming in Python, where I argued for FP-style thinking inside Python, and Where to Start: An FP-Style Engineering Workflow in Python, where I focused on the workflow before code exists. This time I want to make a narrower claim. The same habits seem unusually compatible with coding agents.
I should be careful about how strongly I state this. What follows is still a working hypothesis based on repeated use, not a controlled study and not a claim that I have proven the pattern in production across many teams. Even so, the early results have been positive enough that I think the mechanism is worth naming. My current view is that FP-style, contract-driven design helps coding agents for the same reason it helps humans: it reduces hidden context, narrows the space of valid implementations, and makes behavior easier to verify once code is generated.
This Started As An FP Workflow Question
The idea started as an engineering workflow question.
In the earlier FP posts, I argued for defining domain types early, writing function signatures before writing bodies, making data flow explicit, and keeping state and side effects contained. I came to those habits because they make Python easier to reason about. They also travel well to TypeScript, Go, Java, Rust, and other languages that are not pure FP languages but still let you model data and isolate mutation.
What changed recently is that I began enforcing that workflow more deliberately while working with Codex. If I entered a task with vague data structures, mixed responsibilities, and a lot of state hidden inside service objects, the agent tended to spend more effort on orientation. It had to reconstruct intent from scattered clues. If I gave it explicit domain objects, narrow signatures, and nearby tests, it behaved less like a talented intern dropped into a maze and more like a focused engineer picking up a bounded task.
That observation matters because a lot of present-day agent advice treats code structure as secondary. Sometimes the model can infer the right design from the repository, the prompt, and a few examples. But not all repositories are equally legible. A codebase with visible contracts gives the agent stronger rails. A codebase with implicit state asks it to reverse-engineer the system before it can even begin the requested change.
Old FP Ideas Matter Again
Functional programming was not invented for LLMs. Its core concerns were older and more human: reduce hidden state, make computation easier to reason about, prefer explicit inputs and outputs, and build systems out of composable transformations rather than long chains of mutation. The original problem was cognitive load. As programs grow, reasoning gets harder when values change in place, dependencies are implicit, and behavior depends on who touched shared state two calls earlier.
That motivation feels newly relevant because coding agents operate under a related constraint profile. They are not humans, and I do not want to force that analogy too far. Still, they work with bounded context, incomplete information, and a constant need to infer intent from local structure. They read fragments of a repository, summarize what they saw, call a tool, receive a result, and then decide what to do next. When the system is organized around explicit data flow, the agent gets a cleaner problem. When the system is organized around hidden state and broad mutable surfaces, the agent has to simulate more of the program in its head before it can act safely.
This is one reason I have become more interested in FP-style discipline for mainstream languages than in the language label itself. You do not need Haskell to benefit from explicit contracts, immutability-first habits, or narrow transformation steps. In practice, most professional teams will stay inside hybrid languages. That makes the style question more important, not less.
Agent-Friendly Code Shrinks The Local Problem
The first mechanism is context reduction.
Consider a service built around mutable internal state:
|
|
Suppose the agent is asked to support tax-exempt orders or introduce a new region-specific rule. The code is readable enough, but the change is not local. The agent has to ask several questions before it can trust its own patch. When does default_region change? How long does the service instance live? Is tax_cache shared across requests? Does last_customer_id drive analytics or retry behavior somewhere else? Is fetch_tax_rate() pure, memoized, or stateful? None of those questions is impossible, but they expand the amount of repository state the agent has to recover before it can modify one behavior safely.
Now compare that with a pipeline where the same behavior is expressed as explicit transformations over explicit data. The agent can reason about resolve_tax_policy, quote_order, and the tests around them without needing to reconstruct the whole lifecycle of a long-lived service object. The problem becomes smaller and more local. That matters because agent performance depends heavily on whether the right local view is enough.
I think this is one of the most underappreciated benefits of FP-style design in the agent era: it turns global reasoning problems into local ones. That is useful for humans, and it is especially useful for tools that operate by reading partial context and making stepwise updates.
Contracts Turn Generation Into Guided Search
The second mechanism is search-space reduction.
An under-structured coding task gives an agent too many plausible ways to be wrong. If the domain types are vague, the interfaces are broad, and the tests only cover the happy path, the model is free to invent intermediate representations, smuggle in side effects, or place logic in whichever layer looks convenient. The generated patch may still compile and still miss the actual contract.
FP-style workflow counters that by making the contract visible before the implementation becomes large:
|
|
This code is only illustrative, but it shows the kind of pressure I want. OrderDraft and TaxPolicy tell the agent what data shapes exist. The function signatures tell it where logic probably belongs. The test name tells it at least one invariant that must survive the change. Once those pieces are in place, the task is much closer to “find an implementation that satisfies this contract.”
That is a much better fit for modern coding agents. They are often effective when the loop looks like generate, run tests, inspect failures, repair, and repeat. But that loop needs constraints. Types, signatures, and tests give the loop a target surface. They turn open-ended synthesis into guided search.
This is also why I now think contract definition is one of the most valuable human contributions in agent-assisted engineering. If the interface is crisp, the agent can do a surprising amount of good work inside it. If the interface is vague, the agent spends its intelligence budget guessing what the system is supposed to be.
Less Hidden State Means Fewer Wrong Turns
The third mechanism is reduced state ambiguity.
Side effects multiply paths through the program. Hidden mutation makes behavior depend on order, lifetime, and interaction history. That is already hard on human readers. It is even harder on an agent that may only see the relevant state indirectly through scattered methods, helper functions, and call sites.
In practice, many bad agent patches are not “stupid” in any interesting sense. They are locally reasonable edits made inside an under-specified environment. The model updates one function, but the true behavior also depends on a shared cache, a mutable config object, a background task, or a helper that writes back into a structure it did not create. The patch fails because the actual state machine was bigger than the visible contract.
FP-style design does not require moral purity about side effects. Real systems still talk to databases, call APIs, log events, and write files. The useful habit is to isolate those effects and make state transitions explicit. If most of the system can be read as parse -> validate -> transform -> persist, the agent has fewer invisible branches to trip over. If the side-effect boundary is narrow, the risk surface narrows with it.
This is also where the human benefits remain important. Review becomes less archaeological when intent is already visible in the types, signatures, and tests. The reviewer can spend more time checking correctness and less time reconstructing what the generated patch was trying to accomplish.
Agents Lower The Cost Of Discipline
There is a practical reason this argument feels more relevant now than it did a few years ago. Structured engineering used to carry a larger immediate cost.
Defining domain objects, writing signatures up front, scaffolding tests early, and pulling side effects toward the boundary are all good habits, but they require labor. On a busy team, the temptation is obvious: skip the modeling, write the class, patch the methods, and clean it up later.
Coding agents change the economics of that tradeoff. They are quite good at generating the boring but useful parts of disciplined design: dataclasses, type aliases, adapter functions, straightforward tests, repetitive plumbing, and small refactors that make a pipeline more explicit. That does not remove the need for human judgment, but it makes the bookkeeping cheaper.
This is one reason I suspect FP-style discipline may become more common in mainstream codebases even when the language stays the same. The old objection was often practical rather than philosophical: the structure was nice, but the upfront cost felt hard to justify. If an agent can write much of the scaffolding, the disciplined version becomes easier to afford.
How To Make Code Easier For Agents To Work In
If you want to test this idea in your own codebase, I think a few habits matter more than grand language debates.
- Define domain types early. Named data structures force the system to appear on the page before implementation details start drifting.
- Write function signatures before writing full bodies. A clear signature exposes composition problems early and gives the agent a narrower target.
- Keep functions small and focused. Local reasoning improves when one function owns one transformation or one decision.
- Isolate side effects. Let stateful boundaries exist where they must, but keep them visible and avoid smearing mutation across the whole module.
- Treat tests as behavioral contracts. A good test suite is not only a correctness check after generation; it is also part of the specification the agent is trying to satisfy.
None of this requires a pure functional language. It mostly requires adopting a style that values explicit contracts over implicit convenience. In Python, TypeScript, Go, or Java, that is often enough to change how legible the repository feels to both humans and tools.
Good Structure Now Serves Two Readers
I am still validating this view for myself, and I do not want to oversell it. I cannot honestly say, at this stage, that FP-style design has been proven as the best general recipe for coding agents. What I can say is that the pattern has been strong in my own work: when I enforce FP-style structure in Codex, especially in languages that are not FP-first, the agent tends to need less wandering, make fewer structural guesses, and converge faster on changes that are easy to review.
Agentic coding is pushing an old engineering question into clearer view. Good structure never existed only for elegance. It existed to make systems easier to reason about. What has changed is the audience. The same repository now has to be legible to a human reviewer and to a coding agent operating through partial context, tool calls, and iterative repair.
For that reason, I expect strong agent users to care more and more about code shape, not only prompt shape. The most useful repositories may be the ones where intent is visible, contracts are narrow, and state is hard to hide. FP-style design is not the only path to those properties. It is simply the family of habits that, in my experience so far, lines up unusually well with them.
Resource
To make these ideas more concrete and reusable, I’ve open-sourced a small agent skill called fp-developer. It packages the workflow described here into a shareable set of rules, checklists, and language-specific setups for Python and Rust (as of now), so that coding agents can consistently apply a functional-first style in real repositories. The goal is to make codebases more legible, more contract-driven, and easier for both humans and agents to modify safely. If you are experimenting with agentic coding, you can use this as a starting point or adapt it to your own conventions.