---
id: wiki-2026-0508-refactoring-best-practices
title: Refactoring Best Practices
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [Refactoring, Code Refactoring, Refactoring Patterns]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
verification_status: applied
tags: [refactoring, software-engineering, code-quality, fowler]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
  language: typescript
  framework: agnostic
---

# Refactoring Best Practices

## 매 한 줄
> **"매 behavior-preserving structure change"**. Fowler 1999 의 catalogue + Kerievsky's "Refactoring to Patterns". 매 small, tested steps. 2026 현재 매 AI-aided refactor (Claude / Cursor / Copilot) 의 first-pass extract / rename / inline 을 automate, 매 human 의 architecture-level 의 own.

## 매 핵심

### 매 정의
- **Refactoring**: change internal structure without changing observable behavior.
- **Restructuring** (Fowler): broader, may include behavior change — 매 not pure refactor.
- **Rule of three**: 매 third duplication 의 trigger DRY refactor (Hunt/Thomas).

### 매 catalogue (top-tier)
- **Extract Function / Method**: long body → named subfn.
- **Inline Function**: trivial wrapper → inline.
- **Extract Variable**: rename intermediate expression for clarity.
- **Rename**: variable / function / class / module.
- **Move Function / Field**: across class boundaries.
- **Replace Conditional with Polymorphism**: switch/if-else → strategy / class hierarchy.
- **Introduce Parameter Object**: collapse long arglist into struct.
- **Replace Magic Number with Constant**.
- **Decompose Conditional**: complex if 의 named function.
- **Replace Loop with Pipeline**: imperative loop → map/filter/reduce.
- **Replace Inheritance with Delegation**.
- **Strangler Fig** (Fowler): gradually replace legacy 의 incremental migration.

### 매 prerequisites
- **Tests first**: green test suite. 매 no tests → 매 write characterization tests 먼저.
- **Small commits**: 매 step 의 commit, revertable.
- **Type checker**: TS / mypy / ruff 의 catch breakage cheap.
- **Linter / formatter**: noise removed first (separate commit).
- **Mechanical refactor 의 IDE tooling**: rename / extract 의 IDE-driven > manual.

### 매 응용
1. Pre-feature refactor (make change easy, then make easy change — Beck).
2. Code-review feedback (extract / rename).
3. Legacy migration (strangler fig).
4. Performance refactor (after profiling, not before).
5. AI-aided cleanup (Claude /simplify, Cursor agent refactor).

## 💻 패턴

### Extract Function
```typescript
// before
function printOwing(invoice: Invoice) {
  console.log("***********************");
  console.log("**** Customer Owes ****");
  console.log("***********************");
  let outstanding = 0;
  for (const o of invoice.orders) outstanding += o.amount;
  console.log(`name: ${invoice.customer}`);
  console.log(`amount: ${outstanding}`);
}

// after
function printOwing(invoice: Invoice) {
  printBanner();
  const outstanding = calculateOutstanding(invoice.orders);
  printDetails(invoice, outstanding);
}
function printBanner() { /* ... */ }
function calculateOutstanding(orders: Order[]) {
  return orders.reduce((sum, o) => sum + o.amount, 0);
}
function printDetails(invoice: Invoice, outstanding: number) { /* ... */ }
```

### Replace Conditional with Polymorphism
```typescript
// before
function pay(employee: Employee) {
  switch (employee.type) {
    case "engineer": return baseSalary(employee);
    case "manager":  return baseSalary(employee) + bonus(employee);
    case "salesman": return baseSalary(employee) + commission(employee);
  }
}

// after
abstract class Employee {
  abstract pay(): number;
  baseSalary() { return this.salary; }
}
class Engineer extends Employee { pay() { return this.baseSalary(); } }
class Manager extends Employee  { pay() { return this.baseSalary() + this.bonus(); } }
class Salesman extends Employee { pay() { return this.baseSalary() + this.commission(); } }
```

### Introduce Parameter Object
```typescript
// before
function bookingPrice(start: Date, end: Date, guests: number, roomType: string, breakfast: boolean) { /* ... */ }

// after
interface BookingRequest { start: Date; end: Date; guests: number; roomType: string; breakfast: boolean }
function bookingPrice(req: BookingRequest) { /* ... */ }
```

### Replace Loop with Pipeline
```typescript
// before
const result: string[] = [];
for (const o of orders) {
  if (o.amount > 100) result.push(o.customer.toUpperCase());
}

// after
const result = orders
  .filter(o => o.amount > 100)
  .map(o => o.customer.toUpperCase());
```

### Decompose Conditional
```typescript
// before
if (date.before(SUMMER_START) || date.after(SUMMER_END)) charge = qty * winterRate + winterFee;
else charge = qty * summerRate;

// after
if (notSummer(date)) charge = winterCharge(qty);
else                 charge = summerCharge(qty);
```

### Strangler Fig migration
```typescript
// 매 facade routes traffic, gradually shifts to new
class PaymentFacade {
  constructor(private legacy: LegacyPayments, private modern: ModernPayments, private flag: FeatureFlag) {}
  async charge(req: ChargeRequest) {
    if (this.flag.isEnabled("payments-v2", req.userId)) return this.modern.charge(req);
    return this.legacy.charge(req);
  }
}
// 매 over weeks, ramp flag 0% → 100%, then delete legacy
```

### Characterization test (legacy with no tests)
```typescript
// 매 capture current behavior 먼저, refactor 다음
test("legacy quote engine 매 produce same output", async () => {
  const inputs = await loadFixture("real-traffic-100.json");
  const expected = inputs.map(i => legacyQuote(i));
  // 매 lock current behavior 의 snapshot
  expect(expected).toMatchSnapshot();
});
```

### AI-aided extract (Claude Code)
```text
# 매 prompt:
"Extract the validation logic in src/orders/handler.ts (lines 45-78)
into a pure function in src/orders/validation.ts. Preserve behavior,
add unit tests for the extracted function, keep types strict."
```

## 매 결정 기준
| 상황 | Approach |
|---|---|
| Function >50 lines | Extract Function |
| Same logic 3+ places | Extract + DRY |
| Switch on type code | Replace with Polymorphism |
| 5+ params | Introduce Parameter Object |
| Legacy, no tests | characterization tests 먼저 |
| Behavior + structure change 동시 | split into 2 PRs |
| Performance issue | profile 먼저, then targeted refactor |
| Big-bang rewrite tempted | strangler fig instead |

**기본값**: 매 small step + green test + commit. 매 mechanical refactor (rename, extract) 의 IDE / AI 의 do. 매 architectural decision 의 human + design doc. 매 separate refactor PR from feature PR.

## 🔗 Graph
- 부모: [[Code-Quality]]
- 변형: [[Strangler-Fig]]
- 응용: [[Legacy-Migration]] · [[Technical_Debt|Technical-Debt]]
- Adjacent: [[Design-Patterns]] · [[TDD]] · [[Code-Review]] · [[Pull_Request_PR]]

## 🤖 LLM 활용
**언제**: extract / rename / inline (mechanical), generate characterization tests, suggest decomposition, explain unfamiliar code before refactoring.
**언제 X**: architectural redesign, performance-critical (profile-driven), security refactor — 매 LLM 의 surface plausible-but-wrong restructures. 매 review every diff.

## ❌ 안티패턴
- **Refactor + feature in same PR**: 매 reviewer 의 cannot tell behavior change from cleanup.
- **Big-bang rewrite**: 매 Joel Spolsky's "things you should never do" — strangler fig instead.
- **No tests**: 매 refactor blind — characterization tests 먼저.
- **Premature abstraction**: 매 generalize before duplication appears (rule of three violated).
- **Rename without IDE**: 매 missed reference, broken string literal.
- **Refactor while red CI**: 매 bug hidden in noise — green first.
- **Over-engineering pattern fit**: forcing Strategy / Visitor when if/else 의 fine.

## 🧪 검증 / 중복
- Verified (Fowler "Refactoring" 2nd ed 2018, Beck "TDD" 2002, Feathers "Working Effectively with Legacy Code" 2004, Kerievsky "Refactoring to Patterns" 2004, Spolsky "Things You Should Never Do" 2000).
- 신뢰도 A.

## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — full canonical Fowler catalogue + 2026 AI-aided refactor patterns |
