Every developer has heard “Don’t Repeat Yourself” (DRY)
- How much code reuse is too much?
- Am I over-abstracting?
- Should this be a helper? A util? A service? A hook?
The truth:
- Bad DRY creates more problems than it solves.
- Good DRY makes your code predictable, clean, and future-proof.
DRY is not just removing duplication
- it’s learning what deserves reuse and what deserves separation.
This post walks you through how to write reusable code the right way
Why DRY Matters
Good reuse helps you:
- Fix bugs once instead of 5 times
- Scale faster
- Reduce cognitive load
- Keep your project consistent
- Avoid “copy-paste debt”
But bad reuse leads to:
- Unnecessary abstractions
- Confusing utilities
- One giant file everyone is scared to touch
- “Helper” folders full of black magic
Let’s fix that.
Step 1: DRY Only When the Behavior Repeats (Not the Code)
Most developers refactor too early.
Look at these two functions:
function createUser() {
console.log("Creating user…");
// some logic
}
function createProduct() {
console.log("Creating product…");
// some logic
}
These look similar.
But should they be merged?
❌ No.
The only repeated line is a console log the behavior is different.
DRY is not about removing repeated lines.
- It’s about removing repeated logic.
Step 2: Start With “Local Reuse” Before “Global Reuse”
Most developers jump straight to a /utils folder.
Instead, follow this rules
- If only one feature uses it, keep it inside that feature.
- If two features use it, promote it to a shared place.
Example React structure:
/features
/auth
login.js
useLogin.js
/dashboard
useDashboard.js
/shared
/hooks
useFetch.js ← used by multiple features
You “graduate” code only when needed.
Step 3: Extract Reusable Code the SMART Way
Before extracting shared code, ask:
Will this logic change in different ways depending on the feature?
If yes → keep it local.
Does this logic repeat AND behave the same?
If yes → extract it.
Does extraction make the code clearer or more confusing?
If confusing → stop.
Step 4: Real Examples of Good Reuse
Example: Reusable API Client
Bad:
fetch("/api/users")
fetch("/api/products")
fetch("/api/orders")
Good:
export async function api(url, options = {}) {
return fetch(url, options).then(res => res.json());
}
Step 5: The DRY Traps (Avoid These at All Costs)
Over-abstracting early
Don’t create files “just in case.”
Giant utility folders
If you have a helpers.js with 37 functions, it’s time to rethink.
Mixing unrelated logic
utils/index.js containing API logic, string formatting, date parsing, and a random debounce function.
Creating shared code for one-off logic
Not everything needs reuse.
Step 6: When to Refactor for Reuse
Refactor when:
- Two modules copy-paste the same logic
- Fixing a bug means changing multiple files
- Code feels “the same but slightly different”
- You keep writing similar boilerplate
If you catch yourself repeating work, that’s your signal.
Final Thought
DRY isn’t about having zero duplication, it’s about smart duplication and smarter reuse.
Good code is:
- clear
- predictable
- discoverable
- reusable when needed
- duplicated when necessary
Yes, duplication is sometimes the right choice.
Your goal isn’t to write fancy code.
Your goal is to write code that future you, your teammates, and your project can trust.