SuperAPI AI Directory

dry-refactoring

Coding & development

View repository

Updated 10-16 09:32

About this skill

DRY Standardized Process: From Identification to Refactoring

This skill guides you to systematically apply the DRY (Don't Repeat Yourself) principle, eliminating code duplication through a four-step standardized process to improve code quality and maintainability.

When to Use This Skill

Use this skill when the user requests:

  • Eliminate code duplication or redundancy
  • Refactor code with obvious copy-paste traces
  • Apply the DRY principle to optimize the codebase
  • Identify and fix "code smells" (such as magic numbers, duplicated logic)
  • Extract common logic into reusable units
  • Improve code maintainability

Key trigger words: DRY, duplicated code, code duplication, refactoring, eliminate duplication, copy-paste, magic numbers, code smells, abstraction, extract function

Core idea

Every piece of knowledge in the system must have a single, unambiguous, authoritative representation.

This means:

  • Any business logic, algorithm, or configuration information should exist only inone place
  • in the codebase. If a change is needed, you only need to change thisone place
  • The change will automatically be reflected everywhere that uses this logic

Rule of Two: When you write an almost identical block of code for the second time, alarm bells should ring. This is the signal to start refactoring.

Four-step standardized process

This is a micro-cycle that can be applied at any stage of coding. Strictly follow the steps to ensure the safety and effectiveness of the refactoring.


Step 1: Identify Repetition

Goal: Like a detective, stay vigilant for "code smells" in the code and find all duplication.

1.1 Obvious duplication

Direct copy-paste:

  • Two or more blocks of code look almost exactly the same
  • Only variable names or a few values differ
  • This is the most obvious duplication and the one most in need of elimination

Example:

// 重复 1
function calculateOrderDiscount(orderTotal) {
  if (orderTotal > 100) {
    return orderTotal * 0.1;
  }
  return 0;
}

// 重复 2
function calculateCouponDiscount(couponTotal) {
  if (couponTotal > 100) {
    return couponTotal * 0.1;
  }
  return 0;
}

"Magic numbers" or strings:

  • The same configuration value or string appears in multiple places as a literal
  • For example:0.08、"http://api.example.com"、100

Example:

# 魔术数字重复
def calculate_tax_1(amount):
    return amount * 0.08  # ❌ 魔术数字

def calculate_tax_2(amount):
    return amount * 0.08  # ❌ 再次出现

def calculate_total(amount):
    tax = amount * 0.08   # ❌ 第三次
    return amount + tax

1.2 Semantic duplication

Structural duplication:

  • The code structure is similar, but the specific variable names or values differ
  • Multiple if-else structures all perform similar conditional checks and assignments

Example:

// 结构性重复
function processUserData(user: User) {
  if (user.age >= 18) {
    user.status = 'adult';
  } else {
    user.status = 'minor';
  }
}

function processProductData(product: Product) {
  if (product.price >= 100) {
    product.category = 'premium';
  } else {
    product.category = 'standard';
  }
}

Logical duplication:

  • Two different functions whose code looks different
  • but at the business logic level they achieve the same goal

Example:

// 逻辑重复:都在计算折扣,只是来源不同
function applyMembershipDiscount(price, memberLevel) {
  const discountRates = { gold: 0.2, silver: 0.1, bronze: 0.05 };
  return price * (1 - (discountRates[memberLevel] || 0));
}

function applySeasonalDiscount(price, season) {
  const discountRates = { winter: 0.2, spring: 0.1, summer: 0.05 };
  return price * (1 - (discountRates[season] || 0));
}

Identification checklist

When reviewing code, check for the following signals:

  • Copied-pasted code blocks (completely identical or highly similar)
  • The same numbers and strings appearing in multiple places
  • Similar if-else or switch-case structures
  • Functions with similar functionality but different names
  • The same algorithm reimplemented in different places
  • The same validation logic scattered across multiple places

💡 Action: Use search functionality to find duplicated literals and similar function name patterns. Record all locations where duplication occurs.


Step 2: Abstract the Logic

Goal: Extract the duplicated logic and encapsulate it into an independent, reusable unit.

2.1 Identify Variable and Invariant Parts

Invariant Parts:

  • This is the repeated core logic
  • Code that remains unchanged on each repetition
  • This will become your abstraction body

Variable Parts:

  • Things that change on each repetition
  • Different values, variable names, configurations
  • These will become the function or class'sparameters

Analysis Example:

// 原始重复代码
const userEmail = validateEmail(user.email);
const adminEmail = validateEmail(admin.email);
const supportEmail = validateEmail(support.email);

// 分析:
// 不变部分:validateEmail() 调用
// 可变部分:不同的 email 值

2.2 Choose the Appropriate Abstraction Form

Based on the characteristics of the repetition, choose the most appropriate abstraction method:

Abstraction Form Applicable Scenarios Example
Function Encapsulate a piece of algorithm or behavior Calculate discounts, validate input, format data
Class Encapsulate behavior + associated state User manager, data processor, configuration manager
Module/Component A set of related functions, classes, and configurations Authentication module, logging module, API client
Configuration File/Constants Repeated magic numbers or strings API endpoints, tax rates, thresholds
Higher-Order Functions Repeated control flow or patterns Retry logic, cache wrappers, error handling

2.3 Design the Abstraction Interface

Function Abstraction Example:

# ❌ 重复代码
def process_user_order(user_id, order_data):
    user = db.query(User).filter_by(id=user_id).first()
    if not user:
        raise ValueError("User not found")
    # 处理订单...

def process_user_payment(user_id, payment_data):
    user = db.query(User).filter_by(id=user_id).first()
    if not user:
        raise ValueError("User not found")
    # 处理支付...

# ✅ 抽象后
def get_user_or_error(user_id):
    """不变部分:获取用户并验证"""
    user = db.query(User).filter_by(id=user_id).first()
    if not user:
        raise ValueError("User not found")
    return user

def process_user_order(user_id, order_data):
    user = get_user_or_error(user_id)  # 可变部分:user_id
    # 处理订单...

def process_user_payment(user_id, payment_data):
    user = get_user_or_error(user_id)  # 可变部分:user_id
    # 处理支付...

Constant Abstraction Example:

// ❌ 魔术数字
function calculateTax(amount) {
  return amount * 0.08;
}

function displayTaxInfo(amount) {
  console.log(`Tax (8%): $${amount * 0.08}`);
}

// ✅ 抽象为常量
const TAX_RATE = 0.08;

function calculateTax(amount) {
  return amount * TAX_RATE;
}

function displayTaxInfo(amount) {
  console.log(`Tax (${TAX_RATE * 100}%): $${amount * TAX_RATE}`);
}

Class Abstraction Example:

// ❌ 重复的状态和行为
const userCache = new Map();
function getUserFromCache(id: string) { /*...*/ }
function setUserInCache(id: string, user: User) { /*...*/ }

const productCache = new Map();
function getProductFromCache(id: string) { /*...*/ }
function setProductInCache(id: string, product: Product) { /*...*/ }

// ✅ 抽象为类
class Cache<T> {
  private store = new Map<string, T>();

  get(id: string): T | undefined {
    return this.store.get(id);
  }

  set(id: string, value: T): void {
    this.store.set(id, value);
  }
}

const userCache = new Cache<User>();
const productCache = new Cache<Product>();

Abstraction Design Principles

Do:

  • ✅ Parameterize the variable parts (values, configurations, behavior)
  • ✅ Keep the interface simple (number of parameters ≤ 4)
  • ✅ Use descriptive naming (describe "what to do" rather than "how to do it")
  • ✅ Consider future extensibility (but do not over-design)

Don't:

  • ❌ Create overly generic abstractions ("universal functions")
  • ❌ Premature abstraction (do not abstract when there is only one use)
  • ❌ Ignore performance impact (e.g., unnecessary function call overhead)
  • ❌ Use abstractions that are hard to understand (increase cognitive load)

💡 Action: Create a new function, class, or configuration file, put the "invariant parts" into it, and define the "variable parts" as parameters.


Step 3: Replace the Implementation

Goal: Replace all old duplicate code with the new abstraction unit.

3.1 Systematic Replacement

Steps:

  1. Locate all duplication points: Return to all locations recorded in the first step
  2. Replace one by one: Delete the old code and call the new abstraction
  3. Pass in the correct parameters: Ensure the parameters correspond correctly
  4. Keep behavior consistent: Ensure the functionality is exactly the same before and after replacement

Replacement Example:

Before (duplicate code):

# 位置 1: user_service.py
def create_user(data):
    if not data.get('email'):
        return {'error': 'Email is required'}, 400
    if not data.get('password'):
        return {'error': 'Password is required'}, 400
    # 创建用户...

# 位置 2: product_service.py
def create_product(data):
    if not data.get('name'):
        return {'error': 'Name is required'}, 400
    if not data.get('price'):
        return {'error': 'Price is required'}, 400
    # 创建产品...

After (using abstraction):

# 新抽象: validation_utils.py
def validate_required_fields(data, required_fields):
    """验证必填字段"""
    for field in required_fields:
        if not data.get(field):
            return {'error': f'{field.capitalize()} is required'}, 400
    return None

# 位置 1: user_service.py (已替换)
def create_user(data):
    error = validate_required_fields(data, ['email', 'password'])
    if error:
        return error
    # 创建用户...

# 位置 2: product_service.py (已替换)
def create_product(data):
    error = validate_required_fields(data, ['name', 'price'])
    if error:
        return error
    # 创建产品...

3.2 Handle Edge Cases

Sometimes there are subtle differences between duplicate code that require special handling:

Strategy 1: Add optional parameters

// 大部分重复,但有一个地方需要额外日志
function processData(data, options = {}) {
  // 通用处理...

  if (options.enableLogging) {
    console.log('Processing:', data);
  }

  return result;
}

// 使用
processData(data1);  // 无日志
processData(data2, { enableLogging: true });  // 有日志

Strategy 2: Callback functions

// 核心流程相同,但中间步骤不同
function processWithCustomStep<T>(
  data: T,
  customStep: (item: T) => T
): T {
  // 前置处理
  const prepared = prepare(data);

  // 可变的自定义步骤
  const processed = customStep(prepared);

  // 后置处理
  return finalize(processed);
}

// 使用
processWithCustomStep(userData, (user) => validateUser(user));
processWithCustomStep(productData, (product) => enrichProduct(product));

Strategy 3: Keep special cases

# 如果某个重复有本质上的不同,考虑保留它
def process_standard_order(order):
    return apply_dry_abstraction(order, 'standard')

def process_vip_order(order):
    # VIP 订单有完全不同的业务逻辑,不强行抽象
    # 保留独立实现
    pass

Replacement Checklist

  • Confirm that all duplicate points have been replaced (do not miss any)
  • Delete the old duplicate code (avoid mixing old and new approaches)
  • Check import statements and dependency relationships
  • Ensure parameter order and types are correct
  • All edge cases have been handled

⚠️ Warning: If you only replace part of it, you have created another kind of inconsistency, and the situation may be worse. Make sure to replace all or none.

💡 Action: Use the IDE's "Find All References" feature to ensure no duplicate points are missed.


Step 4: Verify and Test

Objective: Ensure that the refactoring has not broken any functionality, and that the program behavior is exactly the same before and after refactoring.

4.1 Unit Testing

Write independent unit tests for the new abstraction you created:

Test coverage key points:

  • ✅ Correct output for normal input
  • ✅ Boundary value testing (empty values, maximum values, minimum values)
  • ✅ Error handling for abnormal input
  • ✅ Behavior of different parameter combinations

Example:

# 抽象函数
def calculate_discount(price, discount_rate):
    """计算折扣后价格"""
    if not 0 <= discount_rate <= 1:
        raise ValueError("Discount rate must be between 0 and 1")
    return price * (1 - discount_rate)

# 单元测试
def test_calculate_discount():
    # 正常情况
    assert calculate_discount(100, 0.1) == 90
    assert calculate_discount(100, 0) == 100

    # 边界情况
    assert calculate_discount(0, 0.5) == 0
    assert calculate_discount(100, 1) == 0

    # 异常情况
    with pytest.raises(ValueError):
        calculate_discount(100, 1.5)
    with pytest.raises(ValueError):
        calculate_discount(100, -0.1)

4.2 Integration Testing

Run the integration tests that cover the modified code areas:

# 运行特定模块的测试
pytest tests/test_user_service.py
pytest tests/test_product_service.py

# 或运行整个测试套件
npm test
pytest

Check key points:

  • All tests pass
  • No new failures or errors
  • No significant performance degradation
  • Coverage has not decreased

4.3 Manual Verification

If there are no automated tests (or test coverage is insufficient), perform manual verification:

Verification checklist:

  • Launch the application and check whether it runs normally
  • Test the modified functionality (via UI or API)
  • Check whether log output is normal
  • Test error scenarios (invalid input, boundary conditions)
  • Test in different environments (development, testing, staging)

4.4 Performance Verification

Ensure that the abstraction has not introduced performance issues:

import time

# 性能测试
def benchmark_function(func, *args, iterations=1000):
    start = time.time()
    for _ in range(iterations):
        func(*args)
    end = time.time()
    return (end - start) / iterations

# 对比重构前后
old_time = benchmark_function(old_implementation, test_data)
new_time = benchmark_function(new_implementation, test_data)

print(f"Old: {old_time:.6f}s, New: {new_time:.6f}s")
print(f"Difference: {((new_time - old_time) / old_time * 100):.2f}%")

4.5 Code Review

If working in a team, conduct a code review:

Review key points:

  • Is the abstraction reasonable and easy to understand?
  • Are the names clear and consistent with conventions?
  • Are there any missed duplicate points?
  • Is there excessive abstraction or complex design?
  • Are the documentation and comments sufficient?

💡 Action: Run all relevant tests to ensure the program's external behavior is exactly the same before and after refactoring. No tests? Now is the best time to write tests.


Complete Example: From Start to Finish

Scenario: Discount calculation in an e-commerce system

Original Code (with duplication)

// order_service.js
function calculateOrderTotal(order) {
  let total = 0;
  for (const item of order.items) {
    total += item.price * item.quantity;
  }

  // 会员折扣
  if (order.memberLevel === 'gold') {
    total = total * 0.8;  // ❌ 魔术数字
  } else if (order.memberLevel === 'silver') {
    total = total * 0.9;  // ❌ 魔术数字
  }

  return total;
}

// cart_service.js
function calculateCartTotal(cart) {
  let total = 0;
  for (const item of cart.items) {
    total += item.price * item.quantity;  // ❌ 重复计算逻辑
  }

  // 优惠券折扣
  if (cart.couponType === 'premium') {
    total = total * 0.8;  // ❌ 重复的折扣计算
  } else if (cart.couponType === 'standard') {
    total = total * 0.9;  // ❌ 重复的折扣计算
  }

  return total;
}

Step 1: Identify Duplication

Duplication found:

  1. Loop logic for calculating the total price of goods (structural duplication)
  2. Discount calculation logic (logical duplication)
  3. Magic numbers 0.8 and 0.9(obvious duplication)

Step 2: Abstract the Logic

// pricing_utils.js (新建)

// 抽象 1:商品总价计算
function calculateItemsTotal(items) {
  return items.reduce((total, item) => {
    return total + (item.price * item.quantity);
  }, 0);
}

// 抽象 2:折扣配置(消除魔术数字)
const DISCOUNT_RATES = {
  membership: {
    gold: 0.2,    // 20% off
    silver: 0.1,  // 10% off
    bronze: 0.05  // 5% off
  },
  coupon: {
    premium: 0.2,   // 20% off
    standard: 0.1,  // 10% off
    basic: 0.05     // 5% off
  }
};

// 抽象 3:应用折扣
function applyDiscount(amount, discountRate) {
  if (discountRate < 0 || discountRate > 1) {
    throw new Error('Invalid discount rate');
  }
  return amount * (1 - discountRate);
}

// 抽象 4:获取折扣率
function getDiscountRate(category, level) {
  return DISCOUNT_RATES[category]?.[level] || 0;
}

export { calculateItemsTotal, applyDiscount, getDiscountRate };

Step 3: Replace the Implementation

// order_service.js (重构后)
import { calculateItemsTotal, applyDiscount, getDiscountRate } from './pricing_utils.js';

function calculateOrderTotal(order) {
  const subtotal = calculateItemsTotal(order.items);
  const discountRate = getDiscountRate('membership', order.memberLevel);
  return applyDiscount(subtotal, discountRate);
}

// cart_service.js (重构后)
import { calculateItemsTotal, applyDiscount, getDiscountRate } from './pricing_utils.js';

function calculateCartTotal(cart) {
  const subtotal = calculateItemsTotal(cart.items);
  const discountRate = getDiscountRate('coupon', cart.couponType);
  return applyDiscount(subtotal, discountRate);
}

Step 4: Verify and Test

// pricing_utils.test.js
import { calculateItemsTotal, applyDiscount, getDiscountRate } from './pricing_utils.js';

describe('Pricing Utils', () => {
  describe('calculateItemsTotal', () => {
    it('should calculate total for multiple items', () => {
      const items = [
        { price: 10, quantity: 2 },
        { price: 5, quantity: 3 }
      ];
      expect(calculateItemsTotal(items)).toBe(35);
    });

    it('should return 0 for empty items', () => {
      expect(calculateItemsTotal([])).toBe(0);
    });
  });

  describe('applyDiscount', () => {
    it('should apply 20% discount correctly', () => {
      expect(applyDiscount(100, 0.2)).toBe(80);
    });

    it('should throw error for invalid discount rate', () => {
      expect(() => applyDiscount(100, 1.5)).toThrow('Invalid discount rate');
    });
  });

  describe('getDiscountRate', () => {
    it('should return correct membership discount', () => {
      expect(getDiscountRate('membership', 'gold')).toBe(0.2);
    });

    it('should return 0 for unknown level', () => {
      expect(getDiscountRate('membership', 'unknown')).toBe(0);
    });
  });
});

// 运行测试
// npm test pricing_utils.test.js

Refactoring results:

  • ✅ Eliminated all duplicate code
  • ✅ Magic numbers are centrally managed
  • ✅ Each function has a single responsibility
  • ✅ Easy to test and maintain
  • ✅ If you need to add a new membership level or discount type, you only need to modify DISCOUNT_RATES

Common Pitfalls and Solutions

Pitfall 1: Over-Abstraction

Symptoms: An abstraction was created that is too generic and difficult to understand.

Example:

// ❌ 过度抽象
function universalProcessor(data, options, callbacks, config, meta) {
  // 100 行通用处理逻辑...
}

// ✅ 合理抽象
function processUserData(user) {
  return validate(user) && transform(user);
}

Solution:

  • Abstract only when there is clear duplication
  • Keep abstractions simple and clear
  • If there are more than 4 parameters, consider redesigning

Pitfall 2: Incomplete Replacement

Symptoms: Only some duplicate points were replaced, leaving some old code behind.

Consequences:

  • Both old and new implementations exist in the codebase
  • Future modifications are likely to miss some
  • Creates new inconsistencies

Solution:

  • Use global search to ensure all duplicates are found
  • Complete all replacements at once
  • Use a linter or static analysis tools to detect unused code

Pitfall 3: Ignoring Performance Impact

Symptoms: The abstraction introduced unnecessary performance overhead.

Example:

# ❌ 每次调用都重新编译正则表达式
def validate_email(email):
    return re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', email)

# ✅ 复用编译后的正则表达式
EMAIL_PATTERN = re.compile(r'^[\w\.-]+@[\w\.-]+\.\w+$')

def validate_email(email):
    return EMAIL_PATTERN.match(email)

Solution:

  • Benchmark performance-sensitive code
  • Use optimization techniques such as caching and memoization
  • Use profiling tools when necessary

Pitfall 4: Breaking Encapsulation

Symptoms: The abstraction exposed too many internal implementation details.

Example:

// ❌ 暴露内部状态
class UserManager {
  public users: Map<string, User>;  // 直接暴露内部数据结构

  getUser(id: string) {
    return this.users.get(id);
  }
}

// ✅ 隐藏内部实现
class UserManager {
  private users: Map<string, User>;

  getUser(id: string): User | undefined {
    return this.users.get(id);
  }

  addUser(user: User): void {
    this.users.set(user.id, user);
  }
}

Solution:

  • Use access control (private, protected)
  • Provide clear public interfaces
  • Hide implementation details

Best Practices

1. Incremental Refactoring

Do not try to refactor the entire codebase at once:

Strategy:

  • ✅ Refactor one small duplicated area at a time
  • ✅ Test immediately after refactoring
  • ✅ Commit small, atomic changes
  • ✅ Gradually expand the scope of refactoring

Example Workflow:

# 1. 创建特性分支
git checkout -b refactor/dry-pricing-logic

# 2. 重构一个模块
# 编辑 pricing_utils.js

# 3. 测试
npm test

# 4. 提交
git add pricing_utils.js
git commit -m "Extract pricing calculation to reusable utility"

# 5. 重构使用该模块的文件
# 编辑 order_service.js

# 6. 再次测试和提交
npm test
git add order_service.js
git commit -m "Refactor order service to use pricing utility"

# 7. 继续其他模块...

2. Document Your Abstractions

Good abstractions require good documentation:

/**
 * 计算商品折扣后的价格
 *
 * @param price - 原始价格(必须 >= 0)
 * @param discountRate - 折扣率(0-1 之间,0.2 表示 20% 折扣)
 * @returns 折扣后的价格
 * @throws {Error} 如果 discountRate 不在有效范围内
 *
 * @example
 * applyDiscount(100, 0.2) // 返回 80
 * applyDiscount(50, 0) // 返回 50(无折扣)
 */
function applyDiscount(price: number, discountRate: number): number {
  if (discountRate < 0 || discountRate > 1) {
    throw new Error(`Invalid discount rate: ${discountRate}. Must be between 0 and 1.`);
  }
  return price * (1 - discountRate);
}

3. Use the Type System

Leverage the type system to prevent misuse:

// 使用类型别名增强可读性
type DiscountRate = number; // 0-1 之间
type Price = number;        // >= 0

// 更好:使用品牌类型确保类型安全
type DiscountRate = number & { __brand: 'DiscountRate' };

function createDiscountRate(value: number): DiscountRate {
  if (value < 0 || value > 1) {
    throw new Error('Discount rate must be between 0 and 1');
  }
  return value as DiscountRate;
}

function applyDiscount(price: Price, discountRate: DiscountRate): Price {
  return (price * (1 - discountRate)) as Price;
}

// 使用
const rate = createDiscountRate(0.2); // 类型检查通过
applyDiscount(100, rate);

// applyDiscount(100, 0.2); // ❌ 类型错误!必须使用 createDiscountRate

4. Write Tests Before Refactoring

If there are no tests, write tests before refactoring:

// 步骤 1:为现有(重复的)代码写测试
describe('Original Implementation', () => {
  it('should calculate order total correctly', () => {
    const order = {
      items: [{ price: 10, quantity: 2 }],
      memberLevel: 'gold'
    };
    expect(calculateOrderTotal(order)).toBe(16); // 20 * 0.8
  });
});

// 步骤 2:重构代码

// 步骤 3:确保测试仍然通过
// npm test

Checklist

After completing the DRY refactoring, verify the following:

Identification Phase

  • All obvious code duplication has been found
  • Magic numbers and hardcoded strings have been identified
  • Structural and logical duplication has been discovered
  • All locations of duplication have been recorded

Abstraction Phase

  • Variable and invariant parts have been clearly distinguished
  • An appropriate abstraction form (function/class/configuration) has been chosen
  • The abstraction has clear, descriptive naming
  • The number of parameters is reasonable (≤ 4)
  • There is no over-abstraction

Replacement Phase

  • All duplication points have been replaced
  • No old code has been left behind
  • Imports and dependencies are correct
  • All edge cases have been handled

Verification Phase

  • Unit tests have been written
  • All existing tests still pass
  • Manual verification performed (if applicable)
  • No significant performance degradation
  • Code review completed (if working in a team)

Overall quality

  • Code is more readable and maintainable
  • Single Responsibility Principle is followed
  • Changes only need to be made in one place
  • Adequate documentation and comments

Summary

The DRY principle is one of the cornerstones of software engineering. By systematically applying this four-step process, you can:

  1. Identify duplication: Develop sensitivity to code smells
  2. Abstract the logic: Create reusable, maintainable code units
  3. Replace the implementation: Eliminate duplication and unify the implementation
  4. Verify with tests: Ensure the safety of the refactoring

Remember:

  • Do not abstract prematurely (wait until there is clear duplication before abstracting)
  • Do not over-abstract (keep it simple and clear)
  • Take small steps (incremental refactoring is safer than a one-time large refactoring)
  • Tests are your safety net (write tests before refactoring)

The ultimate goal:

Let every piece of knowledge have a single, authoritative representation in the system. When a change is needed, you only change one place, and all places that use that knowledge are automatically updated.

This is the power of DRY.