Data structures whose fields are reassigned across the codebase, with no clear owner of the mutation. Every reader of the data carries a different mental model of when it's valid; the side effect surface expands with every writer, and code that looked correct in isolation breaks when a different writer runs first or runs concurrently.
Fields the agent finds reassigned across multiple files with no obvious owner; reasoning about state at any moment requires completeness-check cost across every writer. The agent generates code against the writer set it discovered, missing concurrent writers and writers in cold paths; downstream reads compose against the agent's partial timeline and fail at runtime.
Mutation happens in one place behind a named function (or returns a new value), so the moment of change is clear. The encapsulating function owns the invariant; downstream readers compose against the guarantee rather than against the raw mutable shape, and concurrent reasoning becomes tractable because the mutation surface is one named call.
Mutation happens behind a named function with a clear contract, or the data is replaced rather than modified — the agent locates every change in one place. The agent's per-edit reasoning step count drops because state transitions verify against the wrapper's contract rather than the full timeline of every writer.
Smellier version
const order = { total: 100 };applyDiscount(order); // mutates totaladdTax(order); // mutates total
Fresher version
const order = { total: 100 };const final = addTax(applyDiscount(order));
Reasoning about state requires tracing every writer; cognitive load and the team's search cost both scale with writer count, and concurrent code becomes a hazard area. Reviewers pass changes that work for the call sequence in front of them; production runs a different sequence and produces behavior the reviewer never considered. The team's debugging cost compounds across the writer set.
The agent cannot answer 'what is this value here?' without modeling the full timeline of writes; concurrent reasoning hits the same verification-surface cost at every read. Edits the agent generates against a partial timeline ship as plausible code that fails under the actual interleaving of writers — a silent bug class the targeted test won't catch.
Encapsulating mutation can force the team to learn a less ergonomic API for what was previously a direct write; immutability has migration cost in performance-sensitive code. The team trades maintenance cost for runtime overhead; in hot paths the trade may invert, and the team must choose between encapsulation discipline and per-operation performance.
Switching to immutable or encapsulated mutation forces the agent to construct new objects on every change; for hot-path mutation this trades reasoning clarity for runtime overhead. The agent pays extra token cost on every write that allocates a new value; in performance-sensitive code the cost surfaces as latency the user notices, not just compute the agent pays.
Bugs stop being 'who set this field?' and become local to the encapsulating method. The team's verification cost drops because the audit surface contracts to one call instead of N writers, and downstream code composes against the encapsulating contract without re-checking the underlying mutation. Concurrent reasoning becomes possible because the moment of change is named.
State changes become locatable; the agent traces one entry point instead of N writers when reasoning about behavior. The verification surface contracts to the wrapper's contract; the agent verifies edits against a single named invariant rather than reconstructing the mutation timeline, and retrieval cost on state-related queries drops sharply.
Wrapping mutable fields with passthrough setters that change nothing — the leaky abstraction hides the same mutation everywhere behind syntactic sugar. Reviewers grant the safety presumption; runtime shows the underlying mutation still leaks through every direct field access that bypassed the setter, and the codebase carries an extra ceremony layer without the protection it advertised.
Wrapping writes in passthrough setters leaves the mutation everywhere while looking encapsulated; the API's signature suggests safety the implementation doesn't deliver, and verification-surface cost stays as broad as before. The agent reads the wrapper's name and reasons as if the contract holds; runtime ships behavior that violates the assumed contract.