AI-generated functions can pass every test and still corrupt your data, because the bug lives between functions, not inside one.
The Details:
- Code review happens function by function: trace the input, check the logic, confirm the output. That process misses in-place mutation of shared state entirely. A normalization helper can look flawless on its own while quietly altering a record that other parts of the pipeline still expect to read unchanged.
- Models default to mutation because altering an object in place is almost always the fastest route from the data they have to the shape they need. Returning a fresh copy means extra allocations and more code, and nothing in a typical prompt signals that the object is shared. Without an explicit cue like a frozen type or a comment marking shared state, the model has no reason to treat the safer path as necessary.
- Unit tests miss this because they build clean, disposable inputs. A fresh dictionary has no history and no other consumers, so mutating it causes no visible harm and the test passes. In production, that same object was created upstream, cached, and awaited by two other processes, so the identical mutation becomes a real failure.
- The fix is to test at the actual seam: freeze the object before the call using a frozen dataclass,
MappingProxyType, or Readonly plus Object.freeze, then assert afterward that nothing changed. One test that enforces this invariant on the real call path catches more than a stack of isolated unit tests, because it checks the guarantee that actually matters once the object is shared.
Bottom Line: Clean-looking functions can still wreck shared state, so review and testing need to ask not just what a function returns, but what it touches along the way.