Skip to main content
CleanCodeMastery

Move Field: Keep Data in the Room Where It Is Used

Learn the Move Field refactoring with an easy school story. Move a piece of data to the class that really uses it, so state and behaviour live side by side.

24 min read Updated June 11, 2026beginner
refactoringsmoving-featuresmove-fieldfeature-envyencapsulationtypescriptcsharp

🏏 The cricket kit in the maths classroom

Walk into the Class 7 maths room of Green Valley School and look at the back corner. Next to the dusters and the spare graph-paper charts sits... a full cricket kit. Bats, pads, stumps, two helmets, and a bag of practice balls.

Why is a cricket kit in the maths classroom? Nobody really remembers. Years ago, the sports room was being painted, the sports teacher Mr. Khan kept the kit here "for a few days", and it simply never left. The maths teacher, Mrs. Sharma, has stopped asking.

Now watch the daily nuisance it creates. Every games period, Mr. Khan sends two boys — usually Deepak and Raju — to the maths room: "Go and get the kit." They knock, Mrs. Sharma pauses her lesson on linear equations, the whole of Class 7 turns to watch, the boys drag the heavy bag across the building, and after the period they drag it all the way back. Mrs. Sharma must keep her cupboard unlocked for outsiders. She is also blamed whenever a ball is missing — for equipment she never uses! Last term, when a helmet strap broke, the sports office actually sent her a note asking her to "look after school property better". And whenever the maths room is rearranged for parent-teacher meetings, the sports department panics: "Careful, our kit is in there."

Count the traffic for one single week: five games periods, two trips each, two boys per trip. Twenty boy-journeys across the building, forty interruptions to algebra (once going, once coming back), and one teacher holding the keys to a cupboard whose contents she has never once touched.

One sensible Monday, the principal Mr. Verma walks the corridor, sees Deepak and Raju struggling with the bag for the third time that week, and asks the obvious question: "Who actually uses this kit?" The sports department — every single day. The maths class — never. So the kit moves to the sports room, two minutes from the field. The boys stop their daily expedition. Mrs. Sharma locks her cupboard in peace, and her algebra lessons run uninterrupted. Mr. Khan checks his own kit, in his own room, and signs for his own missing balls. Equipment lives where it is used.

Figure 1: One week in the life of the cricket kit — misery while it sits in the wrong room, peace once it moves home

In code, data is the cricket kit. A field sometimes sits in one class while a completely different class reads it and writes it every day. Every use means a trip across a class boundary — a public getter here, a setter there, two classes tangled for no reason. The cure is Mr. Verma's cure, and it has a name: Move Field.

🤔 What is Move Field?

Move Field is a refactoring where we relocate a field from one class (the source) to another class (the target) — the class that actually uses that data. It appears in Martin Fowler's Refactoring catalog as one of the basic moves for shifting features between objects, alongside its twin Move Method.

The plan in one breath:

  1. Create the same field in the target class.
  2. Redirect every read and write, one at a time, from the old field to the new one.
  3. Delete the old field when nothing touches it any more.

As always with refactoring, behaviour must not change. The program computes the same answers before and after. What changes is the shape of the code: the data now sits beside the methods that depend on it.

Why does a field's home matter? Think about what a field's location says. When you see annualInterestRate declared inside InterestPolicy, the code is telling you: "interest rates are the policy's business." When the same field sits inside Account, the code is telling a small lie about ownership. Lies in code are expensive — every new programmer must discover the truth the hard way, just as every new teacher at Green Valley had to learn, by surprise, that the cricket kit lives behind the graph-paper charts.

There is also a mechanical cost. A field used mostly from outside forces the holding class to expose public getters and setters. Those wide-open doors invite even more outside code to depend on the field, and slowly the two classes become impossible to separate. Mrs. Sharma's unlocked cupboard is exactly this: a public modifier on something that should have been private all along.

Figure 2: Move Field at a glance — the clue, the single-doorway trick, and the family it belongs to
💡

A simple detective trick: pick a field and list every place that reads or writes it. (Your IDE's "Find Usages" does this in one click.) If the list is dominated by one other class, you have found a cricket kit in a maths room. The field's true home is the class at the top of that usage list — and very often, the meaning of the data agrees with the count.

One more thing to keep in mind: Move Field is frequently the first domino. Once the data moves home, the methods that were stranded near the old location become free to follow it — that is Move Method. Fowler points out that when you move a whole cluster of fields and methods together, you are really doing Extract Class or Inline Class, with Move Field as the working tool inside.

College corner: Move Field is the data-side answer to the same metrics that Move Method fixes. A field accessed mainly from outside its class is a textbook encapsulation violation, and it shows up in metrics as high CBO (Coupling Between Objects — every external read or write is a dependency edge) and high LCOM (Lack of Cohesion of Methods — the holding class's own methods never touch the field, so it shares nothing with them). Detection tools approach it from the method side: a method flagged for Feature Envy via ATFD (Access To Foreign Data) is pointing its finger at exactly the fields that may need to move. When several methods in one class all envy the same foreign field, moving the one field is often cheaper than moving the many methods — one kit shifted beats twenty boy-journeys a week.

📋 When do we need it?

Reach for Move Field when you notice these situations:

1. A field used more by another class than its own. This is the data-side version of Feature Envy. Usually we say a method envies another class's data — but flip it around, and you can say the data is stranded away from its behaviour. Either way, reads and writes keep crossing a class boundary.

2. Two classes that know each other too well. When class A constantly pokes class B's fields and B pokes A's, you have Inappropriate Intimacy. Moving each field to the class that truly drives it reduces how much the two classes must know about each other.

3. Preparing a bigger move. Planning to use Extract Class on a Large Class? The actual work is done field by field and method by method — Move Field is the hammer-and-nail of that job. Similarly, when a Lazy Class is being folded into its user with Inline Class, every one of its fields takes a ride on Move Field.

4. The meaning has shifted. Sometimes the code was right once, but the domain grew. The discount percentage used to be a simple number on Order; now there is a whole DiscountPolicy class, and the percentage clearly belongs inside it. Move Field keeps the code honest as the design evolves — like moving the kit once the sports room's paint has long dried.

Run the detective trick on the busFee field you will meet below, and tabulate the evidence:

Access to busFeeFrom which classRead or write
student.busFee * 4 in termBillForTransportOfficeread
student.busFee += in applyFuelHikeTransportOfficeread + write
anywhere inside Student itselfStudentnone

The owner on paper never touches it; one outsider touches it every time. The same evidence as a chart:

Figure 3: Who actually touches the field — when the outsider's slice dominates, the field is a cricket kit

And when should you pause before moving?

  • Many users, no single home. If four classes use the field roughly equally, moving it to one just changes who complains. The field and its operations may deserve their own new class — that is Extract Class.
  • Persistence and serialisation. A field mapped to a database column or written into a JSON format is anchored to the outside world. Moving it means migrations, converters, and version headaches. Possible, but weigh the cost.
  • One loud method. Do not move a field just because one method used it five times this week. Look at the full picture of reads and writes across the codebase before issuing the transfer certificate.

The decision, plotted. The two questions are: who uses the data, and how anchored is it to the outside world (databases, JSON formats, many sharers)?

Figure 4: Move the field or leave it — outsider-dominated, lightly anchored fields are the easy wins

👀 Before and after at a glance

A tiny TypeScript example. The school's Student class is holding busFee — but the only code that ever touches it lives in TransportOffice.

// BEFORE — the bus fee data sits on Student, used only by TransportOffice
class Student {
  constructor(
    public name: string,
    public rollNumber: number,
    public busFee: number, // who actually uses this?
  ) {}
}
 
class TransportOffice {
  termBillFor(student: Student): number {
    return student.busFee * 4; // reaching into Student every time
  }
 
  applyFuelHike(student: Student, percent: number): void {
    student.busFee += (student.busFee * percent) / 100;
  }
}

busFee is the cricket kit. Student never uses it; TransportOffice reads it, writes it, and bills with it. Move the data to the office that owns the concept:

// AFTER — the transport office keeps its own fee register
class Student {
  constructor(
    public name: string,
    public rollNumber: number,
  ) {}
}
 
class TransportOffice {
  private busFees = new Map<number, number>(); // rollNumber -> fee
 
  setBusFee(rollNumber: number, fee: number): void {
    this.busFees.set(rollNumber, fee);
  }
 
  termBillFor(student: Student): number {
    return (this.busFees.get(student.rollNumber) ?? 0) * 4;
  }
 
  applyFuelHike(percent: number): void {
    for (const [roll, fee] of this.busFees) {
      this.busFees.set(roll, fee + (fee * percent) / 100);
    }
  }
}

Look at the wins. Student shrank to data that is genuinely about a student. The fee — a transport concept — now lives in the transport office, beside the billing and hike rules that use it. And notice the bonus: applyFuelHike no longer needs a student at all; the office updates its own register in one loop, the way Mr. Khan now checks all his kit in one shelf-glance instead of sending runners.

Figure 5: Before — every fee operation crosses the class boundary to reach data stored in the wrong class
Figure 6: After — the fee lives inside TransportOffice, and Student is asked only for identity

🪜 Step-by-step, the safe way

Moving data is more delicate than moving a method, because many places may read and write a field. A method has one body you can copy; a field has dozens of little touches scattered everywhere, and missing even one write leaves two cupboards each holding half the kit. The safe procedure goes through a single doorway.

Figure 7: The single-doorway procedure — encapsulate first, so every later step redirects all users at once

Step 1 — Encapsulate the field first (Self-Encapsulate Field). If the field is public, anyone may touch it directly, and you cannot redirect them all safely. So first, make every access go through one getter and one setter:

// INTERMEDIATE STATE 1 — one doorway for all access
class Student {
  private _busFee: number;
 
  get busFee(): number {
    return this._busFee;
  }
  set busFee(value: number) {
    this._busFee = value;
  }
}

Compile and run the tests. Behaviour unchanged — we only added a doorway. In school terms: before moving the kit, Mr. Verma first ruled that all kit requests must go through Mrs. Sharma's desk — no more boys rummaging in the cupboard directly. Once there is one doorway, redirecting the doorway redirects everyone.

Step 2 — Create the field in the target class. Add the fee storage to TransportOffice (here a map keyed by roll number), plus a setter to fill it. Do not delete anything in Student yet.

Step 3 — Make the old doorway delegate to the new home. This is the clever trick. The old getter and setter stay, but they now forward to the target:

// INTERMEDIATE STATE 2 — old doorway forwards to the new home
class Student {
  constructor(private transport: TransportOffice, public rollNumber: number) {}
 
  get busFee(): number {
    return this.transport.feeOf(this.rollNumber);
  }
  set busFee(value: number) {
    this.transport.setBusFee(this.rollNumber, value);
  }
}

Now there is only one real copy of the data, but every old caller still works. Compile, test, breathe.

Figure 8: During the delegation phase — a caller still knocks on the old door, and the door forwards to the new home

Step 4 — Redirect callers one by one. Visit each place that says student.busFee and change it to talk to TransportOffice directly. Compile and test after each redirection. Small steps mean that if something breaks, you know exactly which change broke it.

Step 5 — Demolish the old doorway. When "Find Usages" shows zero callers on Student's getter and setter, delete them, and delete the constructor link to transport if nothing else needs it. The field has officially moved house.

Step 6 — Let the methods follow. Check the source class for methods that were only there because the field was there. They are now standing in an empty room. Send them home with Move Method.

The full journey of the field, drawn as states — note that every state is a stable, working program:

Figure 9: The lifecycle of a moving field — each state compiles, passes tests, and could be shipped
⚠️

Tests are non-negotiable here. A field has both reads and writes, and a missed write redirection is sneaky: the program keeps running, but two copies of the data drift apart, and you get wrong answers days later. Run the tests after every single redirection, and make sure at least one test changes the field's value and reads it back. If no such test exists, write it before you begin.

🏫 A bigger real-life example

Now the full cricket kit story in TypeScript. The maths classroom is storing sports equipment, and the sports period code keeps reaching into the wrong room.

// BEFORE
class MathsClassroom {
  public charts: string[] = ["Graph paper", "Geometry models"];
  public cricketKit: string[] = ["Bat", "Pads", "Stumps", "Helmet"]; // ?!
 
  teachAlgebra(): string {
    return "Solving equations using " + this.charts.join(", ");
    // note: cricketKit is never used in this class
  }
}
 
class SportsPeriod {
  startMatch(room: MathsClassroom): string {
    // the daily expedition to the maths room
    const kit = room.cricketKit;
    if (!kit.includes("Stumps")) return "Cannot play — stumps missing!";
    return "Match started with: " + kit.join(", ");
  }
 
  addEquipment(room: MathsClassroom, item: string): void {
    room.cricketKit.push(item); // writing into another class's cupboard
  }
}

Count the touches. MathsClassroom itself never opens cricketKit. SportsPeriod reads it and writes it in every method. The data has drifted away from its behaviour. Apply Move Field:

// AFTER
class MathsClassroom {
  public charts: string[] = ["Graph paper", "Geometry models"];
 
  teachAlgebra(): string {
    return "Solving equations using " + this.charts.join(", ");
  }
}
 
class SportsRoom {
  private cricketKit: string[] = ["Bat", "Pads", "Stumps", "Helmet"];
 
  kitList(): string[] {
    return [...this.cricketKit];
  }
 
  addEquipment(item: string): void {
    this.cricketKit.push(item);
  }
 
  hasStumps(): boolean {
    return this.cricketKit.includes("Stumps");
  }
}
 
class SportsPeriod {
  constructor(private room: SportsRoom) {}
 
  startMatch(): string {
    if (!this.room.hasStumps()) return "Cannot play — stumps missing!";
    return "Match started with: " + this.room.kitList().join(", ");
  }
}

Three things to admire in the after picture:

  1. The kit became private. In the before version it had to be public so that outsiders could rummage in it. Now the sports room controls its own cupboard and offers polite questions like hasStumps().
  2. MathsClassroom is honest. Reading it tells you exactly what a maths classroom is about. No surprise sports bag in the corner.
  3. The methods followed the data. addEquipment moved along with the kit — a small Move Method riding on the back of Move Field, exactly as Fowler describes: move the data, and the behaviour comes home too.

And the weekly traffic count, before and after — the same count Mr. Verma did in the corridor, but for code:

Figure 10: Cross-boundary touches on the kit data per week of development — the expedition ends

The two touches that remain go through SportsRoom's public methods — polite knocks on a proper door, not hands inside someone else's cupboard.

College corner: notice what the after version did with the list it returns — kitList() hands back a copy ([...this.cricketKit]), not the live array. This is the difference between moving a field and truly encapsulating it. If you return the live collection, every caller still holds a hand inside your cupboard, and your CBO improvement is partly an illusion. Researchers call this representation exposure; in code review you will hear "don't leak your internals". A Move Field that ends with a leaked mutable reference has moved the kit but left the cupboard unlocked.

💻 The same refactoring in C# and Python

The same dance in C#, with a bank flavour. Account is holding the interest rate, but only InterestCalculator ever uses it.

// BEFORE
class Account
{
    public decimal Balance { get; set; }
    public decimal AnnualInterestRate { get; set; } // stranded data
}
 
class InterestCalculator
{
    public decimal MonthlyInterest(Account account)
    {
        // reaching back into Account for the rate every time
        return account.Balance * account.AnnualInterestRate / 12m;
    }
}

The rate describes the interest rules, not the account. Move it:

// AFTER
class Account
{
    public decimal Balance { get; set; }
}
 
class InterestCalculator
{
    private readonly decimal _annualRate;
 
    public InterestCalculator(decimal annualRate)
    {
        _annualRate = annualRate;
    }
 
    public decimal MonthlyInterest(Account account)
    {
        return account.Balance * _annualRate / 12m;
    }
}

Notice how the dependency narrowed. Before, the calculator needed two things from Account; now it needs only Balance. The account stopped exposing a setter for data it never used, and the rate even became readonly — a stronger guarantee than the old public property could ever give. Moving data home often lets you lock it down in ways the old location could not.

The Python version of the same idea, in miniature:

# BEFORE — the rate is stranded on the account
class Account:
    def __init__(self, balance, annual_rate):
        self.balance = balance
        self.annual_rate = annual_rate  # never used by Account itself
 
class InterestCalculator:
    def monthly_interest(self, account):
        return account.balance * account.annual_rate / 12
 
# AFTER — the rate lives with the rules that use it
class Account:
    def __init__(self, balance):
        self.balance = balance
 
class InterestCalculator:
    def __init__(self, annual_rate):
        self._annual_rate = annual_rate
 
    def monthly_interest(self, account):
        return account.balance * self._annual_rate / 12

🧰 IDE support

ToolSupportHow
IntelliJ IDEA / JetBrains familyStrongSelect field, F6 (Refactor → Move); static fields move fully automatically
JetBrains Rider / ReSharper (C#)StrongMove relocates static fields; Extract Class can carry an instance field with its methods
Visual Studio (plain)ManualUse the safe steps with "Find All References" guiding each redirection
VS Code (TypeScript)ManualRename + "Find All References" are the power tools; the single-doorway technique does the rest

Whatever the tool, the single-doorway idea (encapsulate first, then redirect the doorway) is what keeps the move safe — IDEs simply automate the boring parts of it.

⚖️ Benefits and risks

Benefit ✅Risk / cost ⚠️
Honesty of designThe field's location finally tells the truth about which class owns the conceptChoosing the wrong target tells a new lie — study all usages first
EncapsulationThe old class deletes public getters/setters; the new home can make the field private or readonlyDuring the transition, temporary delegating accessors add a little noise
CouplingReads and writes stop crossing the class boundary every dayIf the source still needs occasional access, a back-reference may appear — keep it temporary
UnblockingFrees stranded methods to follow the data with Move MethodA missed write-site creates two diverging copies of the data — redirect through one doorway
PersistenceA cleaner domain model going forwardORM-mapped or serialised fields drag schema migrations along — budget for them

And the family note: Move Field and Move Method are the two bricks; Extract Class and Inline Class are the buildings made from them. When several fields want to move together to the same new place, stop laying single bricks and use Extract Class. When all of a class's fields are leaving one by one, admit it is Inline Class and finish the job properly. Remember the seesaw those two ride: extract when one class does too much, inline when one does too little — and Move Field is how the weight actually shifts from seat to seat.

College corner: if you want a number to defend your refactoring in a design review, measure afferent and efferent coupling on the two classes. Before the move, Student had an extra afferent dependency (TransportOffice depending in on its field) and exposed extra public surface; after, its public API shrank and the dependency arrow weakened to identity-only (rollNumber). At the system scale, repeated Move Fields are what keep a codebase's instability metric honest: data-holding classes stop being accidental hubs that everything points at. A class that is a hub by design (a true domain entity) is fine; a class that became a hub because three departments dumped their kits in it is the maths room.

🧪 Which smells does it cure?

SmellHow Move Field helps
Feature EnvyThe data side of the cure — instead of moving the envious method, move the envied data home
Inappropriate IntimacyClasses stop poking each other's fields once each piece of state lives with its true owner
Large ClassCarrying stranded fields out (often into a new class) is how the big class slims down
Shotgun SurgeryWhen data and its rules unite in one class, one change edits one file
Lazy ClassDuring Inline Class, Move Field is the tool that carries each field into the host

📦 Quick revision box

+--------------------------------------------------------------+
|                    MOVE FIELD — CHEAT CARD                   |
+--------------------------------------------------------------+
| Story    : cricket kit in the maths room -> sports room      |
| Problem  : field declared in class A, used daily by class B  |
| Detect   : Find Usages — one OTHER class dominates the list  |
| Fix      : encapsulate -> create in target -> old accessor   |
|            delegates -> redirect callers 1-by-1 -> delete    |
| Rule     : data belongs where its BEHAVIOUR lives            |
| Test     : after every redirection; include a write+read test|
| Watch    : missed writes = two drifting copies of the data   |
| Don't    : move ORM/serialised fields casually; move when    |
|            many classes share it equally (try Extract Class) |
| Friends  : Self-Encapsulate Field, Move Method, Extract Class|
+--------------------------------------------------------------+

✏️ Practice exercise

Here is a small canteen system with a field living in the wrong room.

class Student {
  constructor(
    public name: string,
    public rollNumber: number,
    public canteenDiscountPercent: number, // hmm...
  ) {}
}
 
class CanteenCounter {
  billFor(student: Student, amount: number): number {
    const discount = (amount * student.canteenDiscountPercent) / 100;
    return amount - discount;
  }
 
  doubleDiscountDay(student: Student): void {
    student.canteenDiscountPercent *= 2;
  }
}

Your tasks:

  1. List every read and write of canteenDiscountPercent. Which class dominates? Which class never uses it? Sketch your own Figure 3 pie for this field.
  2. Move the field into CanteenCounter using the single-doorway method: encapsulate it on Student first, write the delegating intermediate version, then redirect billFor and doubleDiscountDay, then delete the old accessors. Test after each step (try: amount 100, discount 10% → bill 90; after double-discount day → bill 80). Notice that doubleDiscountDay is a write — this is exactly the redirection that bites people who rush.
  3. After the move, can the discount data become private inside CanteenCounter? Make it so, and make sure any list or map you expose is a copy, not the live one.
  4. Place this field on the Figure 4 quadrant: who uses it, and how anchored is it? Would your answer change if the discount were stored in the school's database against each student record?
  5. Bonus thinking question: suppose the library also starts using the same discount for book fines. Now two classes use the field. Does it still belong in CanteenCounter? Sketch (no code needed) what a small DiscountPolicy class would look like, and name the refactoring you would use to create it.

If your answer to task 5 was "Extract Class", you have connected today's brick to tomorrow's building — exactly how real refactoring works. And if task 4 made you hesitate about the database, good: you have learned that some cricket kits are bolted to the floor, and moving them needs a work order, not just two strong boys.

Frequently asked questions

What is the Move Field refactoring in simple words?
Move Field means shifting a piece of data — a field — from the class where it is declared to the class that actually uses it. You create the field in the new class, redirect every read and write to the new location one by one, and then delete the old field. The program behaves the same; only the data's home changes.
How do I decide where a field truly belongs?
Look at all the reads and writes. If another class touches the field far more than its own class does, that other class is the true home. Also ask the meaning question: which concept does this data describe? An interest rate describes the interest policy, not the account, even if it is stored on the account today.
Why should I encapsulate the field before moving it?
If the field is public, code all over the project may touch it directly, and you cannot redirect those touches safely. Wrapping the field in a getter and setter first (Self-Encapsulate Field) gives you one single doorway. Then redirecting the doorway redirects every user at once, with no one left behind.
What is the connection between Move Field and Move Method?
They are companions. Often a method sits in the wrong class only because a field it needs is stuck there. Move the field first, and the envious methods become free to follow it home using Move Method. Fowler treats them as the two basic operations for moving features between objects.
When should I NOT move a field?
Do not move it if many classes use it equally — then there is no single right home, and Extract Class may be better. Be careful with fields tied to database mappings or serialised formats, because moving them ripples into schemas and stored data. And never move a field just because one method touched it a few times; study the full picture first.

Further reading

Related Lessons