Skip to content
aipatterns.dev

Refactoring techniqueSimplifying Method Calls

Rename Method

Change a method's name to say what it does, using a forwarding alias when callers cannot all be updated at once.

Also known as
Change Function Declaration, Rename Function

Problem

A method's name does not describe what it does. It may describe how it works, use a stale term from an old version of the domain, or be so vague (process, handle, doIt) that every reader opens the body to find out. Comments at call sites try to compensate.

Solution

Give the method a name that states its effect or result in domain terms, and update every caller. For published APIs, keep the old name as a deprecated alias for a transition period.

Before

TypeScript
class SubscriptionService {
  // Moves the plan to the next tier and prorates the charge
  async chk(sub: Subscription, plan: PlanId): Promise<Invoice> {
    const credit = this.unusedCredit(sub);
    sub.plan = plan;
    return this.billing.charge(sub.customerId, this.priceOf(plan) - credit);
  }
}

await subscriptions.chk(sub, "pro");

After

TypeScript
class SubscriptionService {
  async upgradeWithProration(sub: Subscription, plan: PlanId): Promise<Invoice> {
    const credit = this.unusedCredit(sub);
    sub.plan = plan;
    return this.billing.charge(sub.customerId, this.priceOf(plan) - credit);
  }

  /** @deprecated Use upgradeWithProration. Remove after v4. */
  chk(sub: Subscription, plan: PlanId): Promise<Invoice> {
    return this.upgradeWithProration(sub, plan);
  }
}

await subscriptions.upgradeWithProration(sub, "pro");

Why

  • Code is read far more than written. A good name removes a trip into the body for every reader.
  • The explanatory comment becomes unnecessary.
  • Consistent names across classes reveal duplication and let you unify interfaces.

Steps

  1. Check whether the method is declared in a superclass, subclass or interface. A rename must cover every declaration in the hierarchy.
  2. If your tooling supports a safe rename (an IDE or language server rename), use it and run the tests. You are done.
  3. Otherwise, add a new method with the new name and copy the body into it. Make the old method delegate to the new one. Run the tests.
  4. Update callers to the new name, a few at a time, running the tests as you go.
  5. When nothing calls the old name, delete it. For a public API, mark it deprecated and delete it in a later release.

Watch out

  • Text search misses dynamic calls: string-based dispatch, reflection, RPC method names, serialized event names, template bindings. Search for the string, not only the symbol.
  • Renaming a method that is part of a public package, REST route or message contract is a breaking change. Keep the alias.
  • Do not rename to describe the implementation (loopAndSumCharges). Name the outcome (totalCharges).
  • If you cannot find a good name, the method may do two things. Try Separate Query from Modifier or Extract Method first.
  • Rename is its own inverse.

Agent checklist

  • All declarations in the class hierarchy and interfaces were renamed together.
  • String-based references (reflection, routes, configs, mocks) were searched for and updated.
  • Public APIs keep a deprecated alias.
  • The new name describes the result, not the mechanics.

Treats these smells

See also

Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.