Suggest an editImprove this articleRefine the answer for “DRY (don't repeat yourself)”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**DRY (Don't Repeat Yourself)** - every piece of logic has one place where it lives. Change it once and all callers update automatically. Copy-pasting means bugs must be fixed in multiple spots, and you will miss at least one. ```javascript // WET: name format in two places function greet(first, last) { return 'Hi, ' + first + ' ' + last; } function fullName(first, last) { return first + ' ' + last; } // DRY: one place for name logic function fullName(first, last) { return first + ' ' + last; } function greet(first, last) { return 'Hi, ' + fullName(first, last); } ``` **Key:** if you copy-pasted logic, extract it to a function or constant.Shown above the full answer for quick recall.Answer (EN)Image**DRY (Don't Repeat Yourself)** - every piece of logic or knowledge in a system has exactly one place where it lives. ## Theory ### TL;DR - Analogy: one master key that opens all doors vs. copying keys everywhere. One copy breaks and security fails. - Main benefit: change logic once, and everything that depends on it updates automatically. - WET (Write Everything Twice) forces updates in multiple spots, which leads to inconsistencies and bugs from mismatched copies. - Decision rule: if you copy-pasted code or data, extract it to a function, constant, or module. ### Quick example ```javascript // WET: name format exists in two places // Fix one, forget the other = silent bug function userFullName(first, last) { return first + ' ' + last; } function userGreeting(first, last) { return 'Hi, ' + first + ' ' + last + '!'; } // DRY: greeting reuses the single name logic function userFullName(first, last) { return first + ' ' + last; } function userGreeting(first, last) { return 'Hi, ' + userFullName(first, last) + '!'; } // userGreeting('Alice', 'Smith') → 'Hi, Alice Smith!' ``` The WET version has name formatting in two places. Fix a bug in one and forget the other. The DRY version has one place to change. ### WET vs DRY Duplicated code forces updates in multiple spots at once. Miss one and you get inconsistency, like a password validation rule checking 8 characters in a form but 6 in an API handler. DRY centralizes the logic: one function, one constant, one module. Change it there and everything that depends on it updates automatically. The opposite of DRY is sometimes called WET (Write Everything Twice). That name is only half-joking. ### When to extract - Copy-pasting similar logic across functions or files? Extract to a shared function. - Same string or number appearing in multiple files? Move it to a named constant. - Identical validation in a form, an API handler, and a test? Put it in a shared utility. - Repeated API boilerplate across routes? Wrap in a factory function. - Config values scattered across files? Centralize them in one config object. ### Runtime behavior Compilers like V8 or TypeScript execute each copy of duplicated logic independently. A bug in duplicated code runs in every copy separately. Some ESLint rules like `no-dupe-keys` flag specific patterns statically, but most duplication only surfaces at runtime when copies get out of sync. ### Common mistakes **Mistake: over-DRYing trivial code.** ```javascript // Pointless abstraction - adds cognitive load, saves nothing const twiceTwo = multiply(2, 2); ``` Abstract only when logic is reused 3+ times or is genuinely complex. Two functions that look similar today may diverge tomorrow. Forcing them into one abstraction early makes both harder to change later. **Mistake: duplicating for "readability."** ```javascript // WET: adding a new role means updating three spots if (admin || moderator || owner) { ... } // DRY: one place to update const canEdit = ['admin', 'moderator', 'owner'].includes(role); ``` Permission bugs in production often trace back to someone adding a role in one `if` block but not the other two. **Mistake: forgetting data duplication.** ```javascript // Wrong: API version hardcoded in 5 separate files { name: 'API v1', version: '1.0' } // Fix export const API_VERSION = '1.0'; ``` DRY applies to data, not just logic. A version bump should touch one file. **Mistake: two functions with the same name but different logic in different modules.** ```javascript // utils.js has validateEmail() // components/validateEmail.js has a different validateEmail() ``` Import the wrong one and you get failures with no obvious cause. One module, barrel exports, done. ### Real-world usage - React: shared `useAuth` hook across pages instead of repeating auth logic in every component (NextAuth.js pattern). - Express: middleware factory `requireRole('admin')` instead of inline role checks on each route (Passport.js pattern). - Node.js: `path.join(__dirname, 'config')` defined once and imported wherever needed. - Redux: centralized action creators so payload shape lives in one file. - TypeScript: shared `User` interface across NestJS models instead of redefining per file. One thing teams get wrong consistently: they DRY the code but leave the database URL hardcoded in 10 scripts. Data duplication is just as expensive as code duplication. ### Follow-up questions **Q:** What is the opposite of DRY? **A:** WET, which stands for Write Everything Twice (or sometimes "We Haven't Estimated Anything Totally"). It describes code where the same logic lives in multiple places. **Q:** When does DRY hurt more than it helps? **A:** When you abstract too early. Use the rule of three: wait until you have at least three duplications before extracting. Two similar functions may diverge tomorrow, and a forced abstraction makes both harder to change. **Q:** How does DRY relate to SOLID? **A:** DRY supports the Single Responsibility Principle. When logic lives in one place, that place is responsible for it and nothing else. **Q:** In microservices, how do you apply DRY across services without coupling them? **A:** Share pure utility packages (validation schemas, date formatters) freely. Avoid sharing domain logic across bounded contexts because that creates coupling. For domain events, use contracts or event schemas rather than shared code. The senior answer here mentions Domain-Driven Design and acknowledges that duplication across services is sometimes acceptable to keep services independent. ## Examples ### Express.js: shared query fetcher ```javascript // WET: the same query pattern repeats in every route app.get('/users', authMiddleware, (req, res) => { db.query('SELECT * FROM users WHERE active=1').then(users => res.json(users)); }); app.get('/orders', authMiddleware, (req, res) => { db.query('SELECT * FROM orders WHERE active=1').then(orders => res.json(orders)); }); // DRY: one function owns the pattern const fetchActive = (table) => db.query(`SELECT * FROM ${table} WHERE active=1`); app.get('/users', authMiddleware, (req, res) => fetchActive('users').then(res.json.bind(res))); app.get('/orders', authMiddleware, (req, res) => fetchActive('orders').then(res.json.bind(res))); ``` Adding a new route is one line, not a copy of the query string. The `WHERE active=1` condition lives once. Change it to `WHERE status='active'` and every route updates. ### React: custom hook for shared fetch logic ```javascript // WET: fetch logic copy-pasted across components function UserList() { const [users, setUsers] = useState([]); useEffect(() => { fetch('/api/users').then(r => r.json()).then(setUsers); }, []); } // DRY: extracted to a reusable hook function useApiData(url) { const [data, setData] = useState([]); useEffect(() => { fetch(url).then(r => r.json()).then(setData); }, [url]); // url in deps - miss this and you get stale data on every url change return data; } const users = useApiData('/api/users'); const products = useApiData('/api/products'); ``` The dependency array with `url` is where developers slip up. Miss it and the hook fetches the initial URL forever, even if `url` changes. One hook implemented correctly once is safer than five fetch blocks scattered across components.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.