A function on class A that reads only data on class B — A.method() reaches through getters on B to compute something B should compute.
A function's body references foreign-class data more than its own; the agent loading the function must also load the foreign class to verify any change.
Each function lives where its data lives; coupling between modules drops.
Each function lives where its data lives; the agent loads one class to reason about one behavior.
Before the refactoring
class Order {account;isVip() {return this.account.tier === 'gold' && this.account.yearsActive >= 3;}}
After the refactoring
class Account {tier; yearsActive;isVip() { return this.tier === 'gold' && this.yearsActive >= 3; }}class Order { account; }order.account.isVip();
B's internals leak through public surfaces just to support A's method; refactoring B becomes a coordination across A's call sites.
Each call to the envious function pulls a second class into the agent's working context; chained reasoning across the boundary compounds the load.
Dependencies don't always travel cleanly — circular imports surface at the destination, and readers' 'where does this live' map briefly breaks.
Dependencies don't always travel cleanly — circular imports surface at the destination, and the agent's mental map of 'where does this live' briefly breaks until indices refresh.
Modules become more cohesive; tests stay focused; feature-envy patterns disappear.
The function's data sits inside the agent's current reasoning context; verifying behavior touches one class instead of two.
Moving every cross-class read mechanically — including functions that legitimately compose data across multiple classes — fan-outs the move and breaks coherent abstractions.
Mechanical move-the-function-to-the-data on every cross-class read creates a fan-out the agent must follow at every call site — the cure becomes a worse smell than the original.