dry

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

DRY — Don't Repeat Yourself

DRY — 不要重复自己

Before Applying

应用前准备

If
.agents/stack-context.md
exists, read it first. Apply this principle using idiomatic patterns for the detected stack. For framework-specific details, use context7 MCP or web search — don't guess.
如果存在
.agents/stack-context.md
文件,请先阅读它。针对检测到的技术栈,使用符合其惯用模式的方式应用该原则。如需框架特定细节,请使用context7 MCP或进行网络搜索——不要凭空猜测。

Principle

原则核心

Every piece of knowledge in a system should have a single, authoritative representation. When that knowledge changes, you should only need to change it in one place.
系统中的每一项知识都应该有唯一、权威的表达形式。当该知识发生变更时,你只需要在一处进行修改即可。

Why This Matters in Production

生产环境中的重要性

Duplicated logic is a ticking time bomb. When a business rule changes and you update it in one place but miss the copy in another, you get inconsistent behavior that's hard to detect and harder to debug. The more copies exist, the more likely one diverges silently.
DRY is not about eliminating similar-looking code. It's about eliminating duplicated knowledge — the same business rule, the same decision, the same source of truth expressed in multiple places.
重复的逻辑是一颗定时炸弹。当业务规则变更时,如果你只更新了一处却遗漏了另一处的副本,就会导致难以察觉且更难调试的不一致行为。副本越多,就越有可能出现悄无声息的差异。 DRY原则并非要消除看起来相似的代码,而是要消除重复的知识——即同一业务规则、同一决策、同一事实来源在多个地方被重复表达。

Rules

实施规则

  1. Distinguish knowledge duplication from code duplication. Two functions that look identical but represent different business concepts should stay separate. Two functions that encode the same business rule should be unified.
  2. Single source of truth for data. Configuration, constants, schema definitions, and validation rules should each live in exactly one place. Everything else should derive from that source.
  3. Extract when the pattern is stable. Don't extract on the first occurrence — you don't yet know the right shape of the abstraction. Extract when you've seen the pattern repeat with the same semantics at least twice.
  4. Centralize business rules. Tax calculations, permission checks, pricing logic — these must live in one module, not scattered across handlers, frontends, and scripts.
  5. Use code generation over manual sync. If two representations must stay in sync (e.g., API types and client types, schema and documentation), generate one from the other rather than maintaining both by hand.
  1. 区分知识重复与代码重复。两个外观相同但代表不同业务概念的函数应保持独立。而编码同一业务规则的两个函数则应被统一。
  2. 数据的单一权威来源。配置、常量、模式定义和验证规则都应仅存在于唯一位置。其他所有相关内容都应从该来源派生。
  3. 模式稳定时再提取抽象。不要在第一次出现时就进行提取——此时你还不知道抽象的正确形态。当你看到相同语义的模式至少重复出现两次时,再进行提取。
  4. 集中管理业务规则。税费计算、权限校验、定价逻辑等必须集中在一个模块中,而不是分散在处理器、前端和脚本中。
  5. 用代码生成替代手动同步。如果两种表达形式必须保持同步(例如API类型与客户端类型、模式与文档),应通过生成其中一种来保持同步,而非手动维护两者。

Anti-Patterns

反模式

  • Shotgun surgery: Changing one business rule requires edits in 5+ files because the rule is duplicated everywhere
  • Copy-paste-modify: Cloning a function and tweaking it instead of parameterizing the original
  • Parallel hierarchies: Maintaining matching structures in multiple layers (e.g., identical type definitions in backend and frontend that aren't generated from a shared schema)
  • Magic strings repeated across files: The same status code, error message, or config key hardcoded in multiple locations
  • Documentation that restates the code: Comments or docs that repeat what the code says (and inevitably drift out of sync)
  • 霰弹式修改:修改一项业务规则需要编辑5个以上的文件,因为该规则在各处都有重复
  • 复制-粘贴-修改:克隆一个函数并微调,而非对原函数进行参数化处理
  • 平行层级结构:在多个层中维护匹配的结构(例如后端和前端中存在相同的类型定义,但并非从共享模式生成)
  • 跨文件重复的魔法字符串:相同的状态码、错误消息或配置键被硬编码在多个位置
  • 重复代码内容的文档:注释或文档重复代码的内容(最终不可避免地会与代码不同步)

The Wrong Kind of DRY

错误的DRY应用方式

-- Code looks similar but represents different concepts — DO NOT unify
def calculate_shipping_cost(weight, distance):
    return weight * 0.5 + distance * 0.1

def calculate_insurance_premium(value, risk_factor):
    return value * 0.5 + risk_factor * 0.1

-- These are different business rules that happen to share a formula today.
-- They will diverge. Keep them separate.
-- Code looks similar but represents different concepts — DO NOT unify
def calculate_shipping_cost(weight, distance):
    return weight * 0.5 + distance * 0.1

def calculate_insurance_premium(value, risk_factor):
    return value * 0.5 + risk_factor * 0.1

-- These are different business rules that happen to share a formula today.
-- They will diverge. Keep them separate.

The Right Kind of DRY

正确的DRY应用方式

-- Same validation rule duplicated — UNIFY
// in signup handler
if len(password) < 8 or not has_uppercase(password):
    return error("Password too weak")

// in password-reset handler
if len(new_password) < 8 or not has_uppercase(new_password):
    return error("Password too weak")

-- Fix: single source of truth
def validate_password(password):
    if len(password) < 8:
        return error("Password must be at least 8 characters")
    if not has_uppercase(password):
        return error("Password must contain an uppercase letter")
    return ok()
-- Same validation rule duplicated — UNIFY
// in signup handler
if len(password) < 8 or not has_uppercase(password):
    return error("Password too weak")

// in password-reset handler
if len(new_password) < 8 or not has_uppercase(new_password):
    return error("Password too weak")

-- Fix: single source of truth
def validate_password(password):
    if len(password) < 8:
        return error("Password must be at least 8 characters")
    if not has_uppercase(password):
        return error("Password must contain an uppercase letter")
    return ok()

Boundaries

适用边界

  • DRY is about knowledge, not syntax. Two blocks of code that look identical may represent different things. Don't unify them just because they look the same.
  • Premature DRY creates wrong abstractions. A bad abstraction is worse than duplication because it's harder to undo. Wait until you understand the pattern before extracting.
  • Tension with KISS: An overly aggressive DRY refactor can create layers of indirection that are harder to follow than the original duplication. If the shared function needs 5 parameters and 3 boolean flags to handle all cases, the duplication was simpler.
  • Tension with YAGNI: Don't build a generic utility "because we might need it elsewhere." Extract when there are concrete, existing duplicates.
  • Cross-boundary DRY has high cost. Sharing code between services, repos, or teams creates coupling. Sometimes duplication across boundaries is healthier than a shared library that blocks independent deployment.
  • DRY关注的是知识而非语法。两段外观相同的代码可能代表不同的事物,不要仅因为它们看起来一样就将其统一。
  • 过早应用DRY会导致错误的抽象。糟糕的抽象比重复代码更糟糕,因为它更难撤销。在提取抽象前,请先充分理解模式。
  • 与KISS原则的冲突:过度激进的DRY重构可能会创建多层间接结构,比原本的重复代码更难理解。如果共享函数需要5个参数和3个布尔标志来处理所有场景,那么原本的重复代码其实更简单。
  • 与YAGNI原则的冲突:不要‘因为我们可能在其他地方需要它’就构建通用工具。当存在具体的、已有的重复代码时再进行提取。
  • 跨边界应用DRY成本高昂:在服务、仓库或团队之间共享代码会产生耦合。有时,跨边界的重复代码比阻碍独立部署的共享库更健康。

Code Review Checklist

代码评审检查清单

  • Does this change introduce duplicated business logic that already exists elsewhere?
  • Are there magic strings or numbers that should be constants?
  • If this business rule changes, how many files need to be updated?
  • Is there a shared abstraction that's been forced to serve too many masters? (Wrong DRY)
  • Are there manually-synced parallel representations that could be generated?
  • 此次变更是否引入了已存在于其他地方的重复业务逻辑?
  • 是否存在应定义为常量的魔法字符串或数字?
  • 如果此业务规则变更,需要更新多少个文件?
  • 是否存在一个被迫适配过多场景的共享抽象?(错误的DRY应用)
  • 是否存在可通过生成来替代手动同步的平行表达形式?

Related Skills

相关技能

  • yagni: When deciding whether to extract (wait for 3 occurrences)
  • kiss: When DRY abstraction becomes harder to follow than the duplication
  • convention-over-configuration: For eliminating repeated configuration patterns
  • yagni:用于决定是否进行抽象提取(等待至少3次重复出现)
  • kiss:当DRY抽象比重复代码更难理解时适用
  • convention-over-configuration:用于消除重复的配置模式