# Move Method

> Move a method to the class whose data it uses most, and leave a delegating call or update the callers.

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

## Problem

A method lives in one class but reads mostly another class's fields. Every change to that other class's data ripples back to it, and the class it lives in has to know far more than it should.

## Solution

**Move the method to the class whose data it uses, passing in whatever it still needs from the old class.** Then either delete the original or keep it as a one-line delegate.

## Before

```ts
class BillingService {
  constructor(private mailer: Mailer) {}

  lateFee(invoice: Invoice, today: Date): number {
    if (invoice.paidAt) return 0;
    const daysLate = daysBetween(invoice.dueDate, today);
    if (daysLate <= invoice.terms.graceDays) return 0;
    const fee = invoice.balance * invoice.terms.dailyLateRate * daysLate;
    return Math.min(fee, invoice.terms.maxLateFee);
  }

  sendReminder(invoice: Invoice, today: Date): void {
    const fee = this.lateFee(invoice, today);
    this.mailer.send(invoice.customerEmail, `Balance ${invoice.balance}, late fee ${fee}`);
  }
}
```

## After

```ts
class Invoice {
  // ...existing fields: paidAt, dueDate, balance, terms, customerEmail

  lateFee(today: Date): number {
    if (this.paidAt) return 0;
    const daysLate = daysBetween(this.dueDate, today);
    if (daysLate <= this.terms.graceDays) return 0;
    const fee = this.balance * this.terms.dailyLateRate * daysLate;
    return Math.min(fee, this.terms.maxLateFee);
  }
}

class BillingService {
  constructor(private mailer: Mailer) {}

  sendReminder(invoice: Invoice, today: Date): void {
    const fee = invoice.lateFee(today);
    this.mailer.send(invoice.customerEmail, `Balance ${invoice.balance}, late fee ${fee}`);
  }
}
```

## Why

- The fee rule sits next to the data it depends on. Changing `terms` and the fee logic happens in one class.
- `BillingService` stops reaching into invoice internals, which reduces coupling.
- Other callers (the dashboard, the export job) can ask the invoice directly.

## Steps

1. Look at everything the method uses: fields, other methods, parameters. Decide whether related members should move with it.
2. Check subclasses and superclasses of the source for overrides or declarations. Polymorphic methods need extra care.
3. Declare the method in the target class. Copy the body and rewrite references: target fields become `this.x`; source members it still needs become parameters (or pass the source object).
4. Compile the target. Make the original method delegate to the new one. Run the tests.
5. Replace callers one by one to call the target directly, or keep the delegate if many callers exist. Run the tests.
6. Delete the delegate when it has no callers.

## Watch out

- Don't move a method that uses the source class's data as much as the target's. Split it with [Extract Method](https://aipatterns.dev/refactoring/techniques/extract-method.md) first and move only the envious part.
- Moving behavior onto a plain data type (a DTO, a generated API model, a database row type) can be impossible or wrong. Those types may be regenerated or shared across services.
- Watch for overrides. If subclasses override the method, all variants must move together or you break dispatch.
- There is no dedicated inverse; you move it back the same way. If a whole class empties out, consider [Inline Class](https://aipatterns.dev/refactoring/techniques/inline-class.md).

## Related

- Fixes [Feature Envy](https://aipatterns.dev/refactoring/smells/feature-envy.md), [Shotgun Surgery](https://aipatterns.dev/refactoring/smells/shotgun-surgery.md), [Inappropriate Intimacy](https://aipatterns.dev/refactoring/smells/inappropriate-intimacy.md), [Data Class](https://aipatterns.dev/refactoring/smells/data-class.md) and [Message Chains](https://aipatterns.dev/refactoring/smells/message-chains.md).
- Often paired with [Move Field](https://aipatterns.dev/refactoring/techniques/move-field.md).
- [Extract Class](https://aipatterns.dev/refactoring/techniques/extract-class.md) is a series of Move Field and Move Method steps.

## Agent checklist

- [ ] The method uses more of the target's data than the source's.
- [ ] Overrides and interface declarations in the source hierarchy were checked.
- [ ] Source members still needed are passed as parameters, not reached through new back-references.
- [ ] All callers updated or delegating; the delegate is removed when unused.
- [ ] Tests pass after each step.
