# Extract Method

> Move a coherent fragment of a long function into its own well-named function and call it from the original place.

- kind: technique
- category: composing-methods
- aliases: Extract Function
- canonical: https://aipatterns.dev/refactoring/techniques/extract-method
- inspired-by: https://refactoring.guru/extract-method

## Problem

A function does several things in sequence, and you have to read every line to learn what each block is for. Comments mark the blocks ("// compute tax", "// format lines"), which is a sign the blocks want names of their own.

## Solution

**Move the fragment into a new function whose name says what it does, and replace the fragment with a call.** Variables the fragment reads become parameters. A single variable it writes and the caller still needs becomes the return value.

## Before

```ts
function renderInvoice(invoice: Invoice): string {
  let out = `Invoice ${invoice.number}\n`;
  out += `Customer: ${invoice.customer.name}\n\n`;

  // line items
  let subtotal = 0;
  for (const line of invoice.lines) {
    const amount = line.quantity * line.unitPrice;
    subtotal += amount;
    out += `${line.sku.padEnd(12)} ${line.quantity} x ${line.unitPrice} = ${amount}\n`;
  }

  // tax and total
  const tax = Math.round(subtotal * invoice.taxRate);
  out += `\nSubtotal: ${subtotal}\nTax: ${tax}\nTotal: ${subtotal + tax}\n`;
  return out;
}
```

## After

```ts
function renderInvoice(invoice: Invoice): string {
  const subtotal = lineTotal(invoice.lines);
  return (
    renderHeader(invoice) +
    renderLines(invoice.lines) +
    renderTotals(subtotal, invoice.taxRate)
  );
}

function renderHeader(invoice: Invoice): string {
  return `Invoice ${invoice.number}\nCustomer: ${invoice.customer.name}\n\n`;
}

function renderLines(lines: InvoiceLine[]): string {
  return lines
    .map((l) => `${l.sku.padEnd(12)} ${l.quantity} x ${l.unitPrice} = ${l.quantity * l.unitPrice}\n`)
    .join("");
}

function lineTotal(lines: InvoiceLine[]): number {
  return lines.reduce((sum, l) => sum + l.quantity * l.unitPrice, 0);
}

function renderTotals(subtotal: number, taxRate: number): string {
  const tax = Math.round(subtotal * taxRate);
  return `\nSubtotal: ${subtotal}\nTax: ${tax}\nTotal: ${subtotal + tax}\n`;
}
```

## Why

- The top-level function now reads as a summary. Readers drill into a helper only when they need its detail.
- Each helper is testable alone, and `lineTotal` can be reused by the PDF renderer and the API serializer.
- Names replace comments, and names stay accurate because the compiler sees them while it ignores comments.

## Steps

1. Pick a fragment that does one thing you can name. If you cannot name it, pick a smaller or different fragment.
2. Create an empty function with that name. Name it for *what* it does, not *how*.
3. Copy the fragment into it. List every variable the fragment reads from the outer scope; make each one a parameter.
4. Find variables the fragment assigns. If exactly one is used afterward, return it. If several are, extract a smaller fragment first, or use [Replace Temp with Query](https://aipatterns.dev/refactoring/techniques/replace-temp-with-query.md).
5. Replace the original fragment with a call. Compile and run the tests.
6. Look for other copies of the same fragment and replace them with the call too.

## Watch out

- Don't extract when the name you would give is no clearer than the code itself (`addOne(x)` for `x + 1`). The indirection costs more than it saves.
- A fragment that writes many locals resists extraction. Use [Replace Method with Method Object](https://aipatterns.dev/refactoring/techniques/replace-method-with-method-object.md) instead of returning a tuple of five values.
- Watch for early `return`, `break` or `continue` inside the fragment. They change meaning once moved; convert them to a returned flag or value first.
- In hot loops, a function call is usually free after inlining by the JIT or compiler, but measure before arguing either way.
- The inverse is [Inline Method](https://aipatterns.dev/refactoring/techniques/inline-method.md). Use it when a helper's body is as clear as its name.

## Related

- Fixes [Long Method](https://aipatterns.dev/refactoring/smells/long-method.md), [Duplicate Code](https://aipatterns.dev/refactoring/smells/duplicate-code.md), [Comments](https://aipatterns.dev/refactoring/smells/comments.md) and [Feature Envy](https://aipatterns.dev/refactoring/smells/feature-envy.md) (extract, then [Move Method](https://aipatterns.dev/refactoring/techniques/move-method.md)).
- Inverse: [Inline Method](https://aipatterns.dev/refactoring/techniques/inline-method.md).
- [Extract Variable](https://aipatterns.dev/refactoring/techniques/extract-variable.md) is the lighter option for a single expression.
- [Template Method](https://aipatterns.dev/design-patterns/template-method.md) often starts as a series of extracted steps.

## Agent checklist

- [ ] The new function has a name that states intent, and the fragment had no clearer name already.
- [ ] Every outer variable the fragment reads is now a parameter; no hidden shared mutable state was introduced.
- [ ] At most one value is returned; early exits were handled before moving.
- [ ] Other duplicates of the fragment now call the new function.
- [ ] Tests pass unchanged after the extraction.
