Organizing Data
Intent
Section titled “Intent”The organizing data family is about the shape of the values a program carries around and the paths by which code reaches them. A program is only as clear as its data. When a field can be touched from anywhere, when a raw 0.07 floats through three modules with no name, or when a humble string secretly carries rules about how it must be formatted, the data is working against you. These refactorings put a boundary around data, give it a name, and let it grow the behaviour it has been quietly asking for.
The smell
Section titled “The smell”Tangled data shows up in small, recurring ways. The same literal appears in five files and nobody remembers what it means. A list field gets handed out and a distant caller pushes into it, breaking an invariant the owner never agreed to. A phone number lives as a string, so every validation and every reformat is copy-pasted at each use site. None of these are dramatic, but together they make the codebase brittle: a change to how data is stored ripples through every place that read it directly.
flowchart TD
A["Data that is awkward to work with"] --> B{"What is wrong?"}
B -->|"A field is read and written everywhere"| C["Encapsulate Variable"]
B -->|"A bare number or string has no name"| D["Replace Magic Literal"]
B -->|"A list or map field is mutated from outside"| E["Encapsulate Collection"]
B -->|"A primitive is sprouting behaviour"| F["Replace Primitive with Object"]
B -->|"A type code drives branching logic"| G["Replace Type Code with Subclasses"]
C --> H["Data with a clear, guarded shape"]
D --> H
E --> H
F --> H
G --> H What this module covers
Section titled “What this module covers”Ten refactorings, each one tightening the relationship between data and the code that uses it:
- Encapsulate Variable — wrap direct access to a piece of data behind accessor functions, so you have one place to validate, log, or later change the representation.
- Replace Magic Literal — give an unexplained number or string a symbolic name, so the literal documents its own meaning and changes in one spot.
- Encapsulate Collection — stop exposing a mutable list or map field; return a read-only view and offer explicit add and remove methods so the owner keeps control of its contents.
- Replace Primitive with Object — when a primitive value starts attracting validation and formatting rules, promote it to a small value type that holds both the data and its behaviour.
- Replace Type Code with Subclasses — when a type-code field drives
switch-style branching, replace the conditional logic with polymorphism across a small class hierarchy. - Self-Encapsulate Field — route even a class’s own reads and writes of a field through its getter and setter, giving subclasses and validation one seam to hook into.
- Split Variable — when one variable is reassigned to mean two different things, give each purpose its own single-assignment variable.
- Rename Field — when a record or struct field’s name no longer fits its data, rename it and update every user so the name tells the truth again.
- Change Reference to Value (and back) — make a small object an immutable value compared by content when sharing buys nothing, or reverse the move when many places must share one updatable instance.
- Replace Type Code with State/Strategy — when a type code drives behaviour and changes at runtime, hold a swappable State or Strategy object instead of a fixed subclass.
Each lesson shows the same before-and-after in TypeScript, Python, Go, and Rust, then walks the safe, ordered mechanics — running your tests between every step.