Two or more subclasses implement the same method identically — `Manager.name()` and `Engineer.name()` both return `this._name` with no variation.
Methods that subclasses implement identically move to the shared superclass.
Before the refactoring
class Manager extends Employee { name() { return this._name; } }class Engineer extends Employee { name() { return this._name; } }
After the refactoring
class Employee { name() { return this._name; } }class Manager extends Employee {}class Engineer extends Employee {}
Bug fixes must land in every copy; the shared behavior isn't expressed in the hierarchy; new subclasses copy the same boilerplate.
If the methods only superficially resemble each other, pulling up creates a fake-shared abstraction — unify only when behavior is actually identical.
One implementation, one place to fix; subclasses focus on what's actually different.
Pulling up methods that only superficially resemble each other — the parent gets a method whose subclasses each interpret differently, creating a fake-shared abstraction.