# Move Field

> Move a field to the class that uses it most, updating every reader and writer to reach it there.

- kind: technique
- category: moving-features-between-objects
- canonical: https://aipatterns.dev/refactoring/techniques/move-field
- inspired-by: https://refactoring.guru/move-field

## Problem

A field lives in one class but is read and written mostly by another. Every change to how it is used touches both classes, and the owning class carries data it does not care about.

## Solution

**Create the field in the target class, route all access through it, and remove it from the source.**

## Before

```ts
class Tenant {
  constructor(
    public id: string,
    public name: string,
    public monthlyApiQuota: number, // only the billing plan cares
    public plan: BillingPlan,
  ) {}
}

class BillingPlan {
  constructor(public tier: "free" | "pro" | "enterprise", public pricePerMonth: number) {}
}

function remainingQuota(tenant: Tenant, used: number): number {
  return tenant.monthlyApiQuota - used;
}

function upgrade(tenant: Tenant): void {
  tenant.plan = new BillingPlan("pro", 49);
  tenant.monthlyApiQuota = 100_000;
}
```

## After

```ts
class Tenant {
  constructor(public id: string, public name: string, public plan: BillingPlan) {}
}

class BillingPlan {
  constructor(
    public tier: "free" | "pro" | "enterprise",
    public pricePerMonth: number,
    public monthlyApiQuota: number,
  ) {}
}

function remainingQuota(tenant: Tenant, used: number): number {
  return tenant.plan.monthlyApiQuota - used;
}

function upgrade(tenant: Tenant): void {
  tenant.plan = new BillingPlan("pro", 49, 100_000);
}
```

## Why

- Quota changes with the plan, so it belongs to the plan. Upgrading can no longer leave a stale quota behind.
- `Tenant` describes the tenant; `BillingPlan` describes what they pay for.
- Methods that use the field can now follow it with [Move Method](https://aipatterns.dev/refactoring/techniques/move-method.md).

## Steps

1. If the field is public, apply [Encapsulate Field](https://aipatterns.dev/refactoring/techniques/encapsulate-field.md) first so all access goes through a getter and setter.
2. Create the field (and its accessors) in the target class.
3. Decide how the source reaches the target: an existing reference, a new field, or a parameter.
4. Change the source accessors to read and write the target's field. Run the tests.
5. Update callers to use the target directly. Run the tests.
6. Remove the field and accessors from the source.

## Watch out

- Persistence mapping: moving a field between classes often means moving a database column or a JSON key. Plan the data migration and keep old payloads readable.
- If several source objects share one target, the moved field becomes shared. Make sure that is the intended meaning.
- Don't move a field that the source's own invariants depend on. Move the methods that enforce those invariants too, or leave it.
- No inverse exists; move it back the same way. If the target becomes a thin holder, consider [Inline Class](https://aipatterns.dev/refactoring/techniques/inline-class.md).

## Related

- Fixes [Shotgun Surgery](https://aipatterns.dev/refactoring/smells/shotgun-surgery.md), [Feature Envy](https://aipatterns.dev/refactoring/smells/feature-envy.md) and [Temporary Field](https://aipatterns.dev/refactoring/smells/temporary-field.md).
- Pairs with [Move Method](https://aipatterns.dev/refactoring/techniques/move-method.md); together they make up [Extract Class](https://aipatterns.dev/refactoring/techniques/extract-class.md).
- [Self Encapsulate Field](https://aipatterns.dev/refactoring/techniques/self-encapsulate-field.md) makes internal access easier to redirect.

## Agent checklist

- [ ] All reads and writes of the field went through accessors before moving.
- [ ] The source has a reliable path to the target object.
- [ ] Persisted data, serialization and API shapes were migrated or kept compatible.
- [ ] The source no longer declares the field.
- [ ] Tests pass.
