| name | code-refactoring |
| description | Simplify and refactor code while preserving behavior, improving clarity, and reducing complexity. Use when simplifying complex code, removing duplication, or applying design patterns. Handles Extract Method, DRY principle, SOLID principles, behavior validation, and refactoring patterns. |
| metadata | {"tags":"refactoring, code-quality, DRY, SOLID, design-patterns, clean-code, simplification, behavior-preservation","platforms":"Claude, ChatGPT, Gemini, Codex"} |
Code Refactoring
When to use this skill
- Code review: Discovering complex or duplicated code
- Before adding new features: Cleaning up existing code
- After bug fixes: Removing root causes
- Resolving technical debt: Regular refactoring
Instructions
Step 1: Extract Method
Before (long function):
function processOrder(order: Order) {
if (!order.items || order.items.length === 0) {
throw new Error('Order must have items');
}
if (!order.customerId) {
throw new Error('Order must have customer');
}
let total = 0;
for (const item of order.items) {
total += item.price * item.quantity;
}
const tax = total * 0.1;
const shipping = total > 100 ? 0 : 10;
const finalTotal = total + tax + shipping;
for (const item of order.items) {
const product = await db.product.findUnique({ where: { id: item.productId } });
if (product.stock < item.quantity) {
throw new Error(`Insufficient stock for ${product.name}`);
}
}
const newOrder = await db.order.create({
data: {
customerId: order.customerId,
items: order.items,
total: finalTotal,
status: 'pending'
}
});
return newOrder;
}
After (method extraction):
async function processOrder(order: Order) {
validateOrder(order);
const total = calculateTotal(order);
await checkInventory(order);
return await createOrder(order, total);
}
function validateOrder(order: Order) {
if (!order.items || order.items.length === 0) {
throw new Error('Order must have items');
}
if (!order.customerId) {
throw new Error('Order must have customer');
}
}
function calculateTotal(order: Order): number {
const subtotal = order.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
const tax = subtotal * 0.1;
const shipping = subtotal > 100 ? 0 : 10;
return subtotal + tax + shipping;
}
async function checkInventory(order: Order) {
for (const item of order.items) {
const product = await db.product.findUnique({ where: { id: item.productId } });
if (product.stock < item.quantity) {
throw new Error(`Insufficient stock for ${product.name}`);
}
}
}
async function createOrder(order: Order, total: number) {
return await db.order.create({
data: {
customerId: order.customerId,
items: order.items,
total,
status: 'pending'
}
});
}
Step 2: Remove Duplication
Before (duplication):
async function getActiveUsers() {
return await db.user.findMany({
where: { status: 'active', deletedAt: null },
select: { id: true, name: true, email: true }
});
}
async function getActivePremiumUsers() {
return await db.user.findMany({
where: { status: 'active', deletedAt: null, plan: 'premium' },
select: { id: true, name: true, email: true }
});
}
After (extract common logic):
type UserFilter = {
plan?: string;
};
async function getActiveUsers(filter: UserFilter = {}) {
return await db.user.findMany({
where: {
status: 'active',
deletedAt: null,
...filter
},
select: { id: true, name: true, email: true }
});
}
const allActiveUsers = await getActiveUsers();
const premiumUsers = await getActiveUsers({ plan: 'premium' });
Step 3: Replace Conditional with Polymorphism
Before (long if-else):
class PaymentProcessor {
process(payment: Payment) {
if (payment.method === 'credit_card') {
const cardToken = this.tokenizeCard(payment.card);
const charge = this.chargeCreditCard(cardToken, payment.amount);
return charge;
} else if (payment.method === 'paypal') {
const paypalOrder = this.createPayPalOrder(payment.amount);
const approval = this.getPayPalApproval(paypalOrder);
return approval;
} else if (payment.method === 'bank_transfer') {
const transfer = this.initiateBankTransfer(payment.account, payment.amount);
return transfer;
}
}
}
After (polymorphism):
interface PaymentMethod {
process(payment: Payment): Promise<PaymentResult>;
}
class CreditCardPayment implements PaymentMethod {
async process(payment: Payment): Promise<PaymentResult> {
const cardToken = await this.tokenizeCard(payment.card);
return await this.chargeCreditCard(cardToken, payment.amount);
}
}
class PayPalPayment implements PaymentMethod {
async process(payment: Payment): Promise<PaymentResult> {
const order = await this.createPayPalOrder(payment.amount);
return await this.getPayPalApproval(order);
}
}
class BankTransferPayment implements PaymentMethod {
async process(payment: Payment): Promise<PaymentResult> {
return await this.initiateBankTransfer(payment.account, payment.amount);
}
}
class PaymentProcessor {
private methods: Map<string, PaymentMethod> = new Map([
['credit_card', new CreditCardPayment()],
['paypal', new PayPalPayment()],
['bank_transfer', new BankTransferPayment()]
]);
async process(payment: Payment): Promise<PaymentResult> {
const method = this.methods.get(payment.method);
if (!method) {
throw new Error(`Unknown payment method: ${payment.method}`);
}
return await method.process(payment);
}
}
Step 4: Introduce Parameter Object
Before (many parameters):
function createUser(
name: string,
email: string,
password: string,
age: number,
country: string,
city: string,
postalCode: string,
phoneNumber: string
) {
}
After (grouped into object):
interface UserProfile {
name: string;
email: string;
password: string;
age: number;
}
interface Address {
country: string;
city: string;
postalCode: string;
}
interface CreateUserParams {
profile: UserProfile;
address: Address;
phoneNumber: string;
}
function createUser(params: CreateUserParams) {
const { profile, address, phoneNumber } = params;
}
createUser({
profile: { name: 'John', email: 'john@example.com', password: 'xxx', age: 30 },
address: { country: 'US', city: 'NYC', postalCode: '10001' },
phoneNumber: '+1234567890'
});
Step 5: Apply SOLID Principles
Single Responsibility:
class User {
constructor(public name: string, public email: string) {}
save() {
}
sendEmail(subject: string, body: string) {
}
generateReport() {
}
}
class User {
constructor(public name: string, public email: string) {}
}
class UserRepository {
save(user: User) {
}
}
class EmailService {
send(to: string, subject: string, body: string) {
}
}
class UserReportGenerator {
generate(user: User) {
}
}
Output format
Refactoring Checklist
- [ ] Function does one thing only (SRP)
- [ ] Function name clearly describes what it does
- [ ] Function is 20 lines or fewer (guideline)
- [ ] 3 or fewer parameters
- [ ] No duplicate code (DRY)
- [ ] if nesting is 2 levels or fewer
- [ ] No magic numbers (extract as constants)
- [ ] Understandable without comments (self-documenting)
Constraints
Mandatory Rules (MUST)
- Test first: Write tests before refactoring
- Small steps: Change one thing at a time
- Behavior preservation: No functional changes
Prohibited (MUST NOT)
- Multiple tasks simultaneously: No refactoring + feature addition at the same time
- Refactoring without tests: Risk of regression
Best practices
- Boy Scout Rule: Leave code cleaner than you found it
- Refactoring timing: Red-Green-Refactor (TDD)
- Incremental improvement: Consistency over perfection
- Behavior preservation: Refactoring involves no functional changes
- Small commits: Commit in focused units
Behavior Validation (Code Simplifier Integration)
Step A: Understand Current Behavior
Fully understand current behavior before refactoring:
## Behavior Analysis
### Inputs
- [list of input parameters]
- [types and constraints]
### Outputs
- [return values]
- [side effects]
### Invariants
- [conditions that must always be true]
- [edge cases]
### Dependencies
- [external dependencies]
- [state dependencies]
Step B: Validate After Refactoring
npm test -- --coverage
npx tsc --noEmit
npm run lint
npm test -- --updateSnapshot
Step C: Document Changes
## Refactoring Summary
### Changes Made
1. [Change 1]: [reason]
2. [Change 2]: [reason]
### Behavior Preserved
- [x] Same input → same output
- [x] Same side effects
- [x] Same error handling
### Risks & Follow-ups
- [potential risks]
- [follow-up tasks]
### Test Status
- [ ] Unit tests: passing
- [ ] Integration tests: passing
- [ ] E2E tests: passing
Troubleshooting
Issue: Tests fail after refactor
Cause: Behavior change occurred
Solution: Revert and isolate the change, then retry
Issue: Code still complex
Cause: Multiple responsibilities mixed in one function
Solution: Extract into smaller units with clear boundaries
Issue: Performance regression
Cause: Inefficient abstraction introduced
Solution: Profile and optimize the hot path
Multi-Agent Workflow
Validation & Retrospectives
- Round 1 (Orchestrator): Validate behavior preservation checklist
- Round 2 (Analyst): Complexity and duplication analysis
- Round 3 (Executor): Test or static analysis verification
Agent Roles
| Agent | Role |
|---|
| Claude | Refactoring plan, code transformation |
| Gemini | Large-scale codebase analysis, pattern detection |
| Codex | Test execution, build verification |
Workflow Example
ask-gemini "@src/ extract list of high-complexity functions"
codex-cli shell "npm test && npm run lint"
References
Metadata
Version
- Current Version: 1.0.0
- Last Updated: 2025-01-01
- Compatible Platforms: Claude, ChatGPT, Gemini
Related Skills
Tags
#refactoring #code-quality #DRY #SOLID #design-patterns #clean-code
Examples
Example 1: Basic usage
Example 2: Advanced usage