skip to content

Explain the Feature Envy and Inappropriate Intimacy code smells: how do you recognise each, how do they differ, and what refactorings address them?

level: middleimportance: must knowfreq 71%

answer

  1. method loves another object's getters
  2. Tell, Don't Ask
  3. Move Function to the data it uses most
  4. two classes reaching into each other's privates
  5. Strategy/Visitor = envy on purpose

basics

~20 s

Feature Envy: a function is more interested in another object's data than its own — it keeps calling that object's getters. Inappropriate Intimacy: two classes know too much about each other's internals. Fix by moving behaviour to the data (Move Function) or separating the classes.

solid answer

~50 s

Both are coupling smells about behaviour sitting in the wrong place. **Feature Envy** — a function in class A spends most of its body reading fields or calling getters of class B. The tell: the method mentions another object more often than its own. It defeats encapsulation because B's data layout leaks into A; a change to B ripples into A. Fix with Move Function (move the whole method to B), or Extract Function then Move Function when only part of the body is envious. Where the body envies several objects, move it to the object whose data it uses most. **Inappropriate Intimacy** — two classes are entangled: they reach into each other's private parts, mutate each other's state, or depend bidirectionally. Fix with Move Function/Move Field to consolidate responsibility, Change Bidirectional Association to Unidirectional, Extract Class for the shared concept, Hide Delegate, or Replace Inheritance with Delegation when a subclass over-uses its parent's internals. Both express Tell-Don't-Ask: send the object a message rather than interrogating it.

code

pseudocode · 12 lines
pseudocode
// Feature Envy: Invoice reaches into Customer
class Invoice {
  discount() {
    tier = customer.getTier()
    years = customer.getYearsActive()
    return tier == GOLD || years > 5 ? 0.1 : 0.0
  }
}

// After Move Function: behaviour lives with its data
class Customer { discountRate() { ... } }
class Invoice  { discount() { return customer.discountRate() } }

go deeper

for a junior

Define both, give the getter-heavy method as the recognisable sign of Feature Envy, and name Move Function as the fix.

for a middle

Add detection heuristics (count references to self vs other), name Extract Function + Move Function for partial envy, and list the forms of Inappropriate Intimacy such as bidirectional associations and subclass access to parent internals.

for a senior

Discuss when envy is correct by design (Strategy, Visitor, layered architecture, functional pipelines over records), the balanced-data case, and how untreated envy escalates into intimacy.

for a principal

Connect to module boundaries and change axes: whether to co-locate behaviour with data depends on which varies faster, and on where the architecture wants its seams (published interfaces, module APIs, anti-corruption layers).

## The shared principle Both smells come from the same design rule: **behaviour should live with the data it operates on**. Fowler's phrasing is that objects should "keep data and the behaviour that uses that data" together; the Pragmatic Programmers' phrasing is **Tell, Don't Ask** — instruct the object to do the work rather than pulling out its state and doing the work on its behalf. ## Feature Envy **Definition.** A function is more interested in *another* module's data than in the data of the module it lives in. **How to spot it mechanically:** count the references. If the body of `A.doThing()` calls `b.getX()`, `b.getY()`, `b.getZ()` and touches almost nothing of `A`, the method envies `B`. **Why it hurts.** - *Encapsulation is nominal only.* `B` has private fields but exposes them through getters, so its representation is effectively public. Changing `B`'s internals breaks `A`. - *Duplication follows.* Once `B`'s getters are public, other classes compute similar things from them; the same rule ends up in several envious callers. - *Cohesion drops.* `A`'s responsibilities blur. **Refactorings.** 1. **Move Function** — the whole body belongs on `B`; move it and let `A` call `b.doThing()`. 2. **Extract Function + Move Function** — only a slice of the body is envious. Extract that slice first, then move the extracted function. 3. **Move Field** — sometimes the data is on the wrong class, not the behaviour. **When Feature Envy is acceptable.** Deliberate architectural separation of data and behaviour makes it a non-smell: - Strategy, Visitor, and similar patterns *intentionally* place behaviour away from the data so that the behaviour can vary independently. - Layered/hexagonal designs keep serialization, presentation, and reporting logic out of domain objects on purpose — putting formatting into the domain entity would be worse. - Functional-style pipelines over immutable records/DTOs are legitimately "envious" by construction. The question is always: *which change axis do you want to make cheap?* If behaviour varies more than data, separating them is right. ## Inappropriate Intimacy **Definition.** Two classes are excessively coupled: they access each other's private or internal members, mutate each other's state, or hold mutual references so that neither can be understood or tested alone. **Common forms.** - *Bidirectional association* — `Order` holds `Customer`, `Customer` holds a list of `Order`. Both sides must be kept consistent on every mutation; lifecycle and deletion become fragile. - *Language-level privilege abuse* — friend declarations, package-private/internal access, reflection, or same-package field access used to bypass an interface. - *Subclass intimacy* — a subclass depends on the parent's internal fields and call ordering (a fragile base class); the parent cannot be changed safely. - *Test intimacy* — tests asserting on internals, so any refactoring breaks the tests. **Refactorings.** - **Move Function / Move Field** so the state and the code that uses it end up on one side. - **Change Bidirectional Association to Unidirectional** — decide which side owns the link. - **Extract Class** — the shared knowledge is a missing concept; give it its own class that both depend on. - **Hide Delegate** — stop letting the caller navigate through to a third object. - **Replace Inheritance with Delegation** — converts a privileged, whitebox relationship into a narrow, blackbox one. ## Distinguishing the two - Feature Envy is **directional and method-scoped**: one method, in the wrong place. Fix is usually a single Move Function. - Inappropriate Intimacy is **mutual and class-scoped**: two types entangled. Fix is a boundary redesign, often introducing a new concept. - Feature Envy left untreated grows into Inappropriate Intimacy: enough envious methods on both sides and the two classes become inseparable. ## Related smells - **Message Chains** (`a.getB().getC().getD()`) is a navigational cousin: the caller knows the whole object graph. - **Middle Man** is the over-correction: a class that only delegates and adds nothing. - **Data Class** — a class with only fields and accessors and no behaviour — is the mirror image of Feature Envy: the envied class is the one that was emptied out.

  • A method uses data from two different objects roughly equally. Where should it go?
    Fowler's rule of thumb is to move it to the object whose data it uses *most*; if it is genuinely balanced, first Extract Function to split the body along the data it touches, then move each piece to its owner. If the method really expresses a relationship between the two, it may belong on a third, newly extracted class (or a domain service) that owns the interaction.
  • How does a Data Class relate to Feature Envy?
    They are two views of the same defect. A Data Class holds fields plus accessors and no behaviour; every rule about that data therefore lives elsewhere, in envious callers. Curing them is the same move: pull behaviour from the callers into the data class until it earns real methods.

Feature Envy is a colleague who keeps walking to your desk to read your notes and do your job from them; Inappropriate Intimacy is two colleagues who share one desk drawer and can no longer work apart.

saying these in an interview costs you the question

  • Claiming any use of another object's getter is Feature Envy — the smell is about the *balance* of references within a method
  • Treating Strategy, Visitor, mappers, and serializers as smells rather than as deliberate separations of behaviour from data
  • "Fixing" Feature Envy by adding more getters, or by making the fields public
  • Confusing Inappropriate Intimacy with any high coupling — the smell specifically concerns access to internals and mutual dependence
  • Replacing intimacy with a pure pass-through wrapper, producing the Middle Man smell instead

context