Moving Features
Intent
Section titled “Intent”The moving features family is about location. The composing-methods moves reshape the inside of a function; these moves decide which class or module a function, a field, or a whole cluster of behaviour should live in. The program does the same thing afterwards — but each piece of work now sits next to the data and the collaborators it actually uses.
The smell
Section titled “The smell”Software decisions made on day one rarely survive contact with day one hundred. A function that once belonged on Order slowly grows to read mostly from Customer. A field added to a record gets touched everywhere except that record. A small class quietly absorbs a second responsibility until it is two classes wearing one name. When behaviour and the data it depends on drift apart, every change means hopping between files. The moves in this module pull them back together.
flowchart TD
A["A feature sits in the wrong place"] --> B{"What is misplaced?"}
B -->|"A function leans on another context"| C["Move Function"]
B -->|"A field is used elsewhere more"| D["Move Field"]
B -->|"One class does two jobs"| E["Extract Class"]
B -->|"A class no longer earns its keep"| F["Inline Class"]
B -->|"Callers navigate an object chain"| G["Hide Delegate"]
C --> H["Each module owns the right work"]
D --> H
E --> H
F --> H
G --> H What this module covers
Section titled “What this module covers”Nine refactorings, each reversible:
- Move Function — when a function references another object’s data and methods more than its own (Feature Envy), relocate it to the class it envies.
- Move Field — when a field is read and written from a different record more than its current home, move it to where it is used.
- Extract Class — when one class has grown to do two distinct jobs (a Large Class), peel one responsibility into a new class.
- Inline Class — the inverse: when a class no longer pulls its weight, fold it back into the class that uses it.
- Hide Delegate (and its inverse, Remove Middle Man) — encapsulate a delegation so callers stop walking object chains, honouring the Law of Demeter.
- Move Statements into Function — when the same statement always runs beside a call, fold it into the function so callers cannot forget it.
- Move Statements to Callers — the inverse: when a function does one job too many for some callers, push the varying statements out to them.
- Split Loop — when one loop does two unrelated jobs, split it into two single-purpose loops for clarity.
- Combine Functions into Class — when functions keep passing the same data clump around, gather them into a class that holds that data as fields.
Each lesson shows the same before-and-after in TypeScript, Python, Go, and Rust, then walks through safe, ordered mechanics — running your tests between every step.