What Is the DRY Principle?
A calm, thorough walk through “Don’t Repeat Yourself” — one of the oldest and most quoted rules in software design — what it truly means, why it is so often misunderstood, and how to apply it wisely instead of mechanically.
The Big Idea, in One Breath
Imagine you keep your home address written down in five different notebooks — one by the phone, one in your school bag, one taped inside a cupboard, one in a diary, and one on a sticky note in your wallet. The day you move to a new house, you now have five separate notebooks to find and correct. Miss even one, and a letter, a delivery, or a friend trying to visit ends up at the wrong door. The problem was never that you wrote your address down. The problem is that you wrote the exact same fact down five separate times, and now every single copy has to be remembered and updated together, forever, or things quietly start going wrong.
That small, everyday headache is exactly the problem a principle called DRY — short for Don’t Repeat Yourself — was created to solve inside software. The idea is almost embarrassingly simple to say out loud: every important fact, rule, or piece of logic in a system should live in exactly one place. Not copied into two files “just to be safe.” Not pasted into three different screens because it was faster than figuring out how to share it properly. One true home for each idea, with everything else in the system simply pointing back to that one home whenever it needs to use the idea.
What makes DRY worth an entire guide, rather than a single sentence, is that this simple-sounding idea turns out to be surprisingly easy to misunderstand, surprisingly easy to misapply, and surprisingly deep once you start looking closely at what actually counts as “the same fact” being repeated. This guide walks through all of that, patiently, with plenty of everyday pictures along the way.
Think about a school with one official calendar posted on the notice board, listing every holiday for the year. Everyone — teachers, students, parents — checks that one calendar. Now imagine instead that every single teacher kept their own separate, handwritten list of holidays, copied from memory at the start of the year. The moment a holiday gets moved, only the teachers who happen to hear about the change update their personal list. The rest keep teaching from an outdated copy, and confusion quietly spreads. A single, shared, official calendar is DRY. Dozens of separately maintained personal copies are the opposite.
It is worth saying plainly, right at the start, that DRY is not really about typing less. A programmer who avoids duplication purely to save keystrokes has missed the point entirely. The real prize is trust — the confidence that when you look at the one official place a fact lives, you are looking at the truth, the whole truth, and nothing but an outdated echo of it. That confidence, once lost across a large system full of scattered, disagreeing copies of the same information, is remarkably hard and expensive to rebuild.
What DRY Actually Means
The formal description of DRY, as its creators originally wrote it, comes down to one core idea: every piece of knowledge in a system deserves a single, clear, official place where it lives, rather than being scattered across the system in several separately maintained copies. Notice the careful choice of the word “knowledge” here, rather than simply “code” — this distinction turns out to matter enormously, and we will spend real time unpacking it later in this guide.
In everyday practice, this usually shows up as a habit: whenever you notice the same calculation, the same rule, the same piece of text, or the same decision appearing in more than one spot, that repetition is a signal worth paying attention to. It does not automatically mean something is broken. It means it is worth asking a simple question: is this really the same underlying fact, expressed twice, or does it just happen to look similar right now by coincidence?
DRY Is About Knowledge, Not Just Text
This is the single most important, and most commonly missed, detail about DRY. Two blocks of code can look completely different on the screen and still violate DRY, if they both encode the exact same underlying business rule. And two blocks of code can look nearly identical, letter for letter, and still be perfectly fine, if they represent two genuinely separate facts that simply happen to be calculated the same way today, by coincidence, and are free to diverge from each other tomorrow.
DRY does not say “never type the same characters twice.” It says “never let the same true fact about your system live in more than one place that has to be kept in sync by hand.”
A useful habit for spotting real knowledge duplication is to ask a simple question about any two similar-looking pieces of code or documentation: if the underlying business rule changed tomorrow, would both places need to change together, for the same reason, every single time? If the honest answer is yes, you are almost certainly looking at the same piece of knowledge wearing two different outfits. If the honest answer is “maybe, but maybe not,” that uncertainty itself is worth sitting with for a while rather than rushing to merge the two right away — a theme this guide returns to later when discussing the risks of moving too fast.
Where DRY Came From
The name “DRY” was given to this idea by two well-known software writers, Andy Hunt and Dave Thomas, in their widely read 1999 book about professional software habits. The book was not a dry, academic textbook — it was written as a collection of hard-won, practical lessons from years of real project work, and DRY was one of the central ideas running through nearly every chapter.
What is interesting, and worth knowing, is that Hunt and Thomas deliberately described DRY as reaching far beyond just program code. In their original writing, they explicitly applied the same idea to database designs, test plans, build processes, and even ordinary project documentation. To them, DRY was never really a coding trick — it was a broader discipline about how a whole team manages shared knowledge, with code being just one of many places that knowledge happens to live.
The underlying instinct behind DRY is actually much older than 1999. Long before the phrase existed, database designers were already practicing something very similar through a technique called normalization, developed decades earlier, which organizes data specifically to avoid storing the same fact in more than one table. Around the same era, other influential software writers were independently arguing that systems should be broken into small, independent modules specifically to avoid scattering the same logic across a program. DRY did not invent the underlying wisdom — it gave that long-standing wisdom a short, memorable name that stuck.
In a later interview reflecting on the book, Thomas made a point that helps explain why DRY resonated so strongly with working engineers: he observed that almost all programming is really maintenance programming, since even brand-new code is being revisited, adjusted, and corrected within minutes of first being written. If maintenance really is the dominant activity in a programmer’s life, then anything that makes maintenance easier — like knowing a fact lives in exactly one findable place — pays for itself many times over across the life of a project. This framing helps explain why DRY was never meant as a narrow coding trick, but as a philosophy about respecting your own future self, and every teammate who will maintain the system after you.
The Opposite: WET Code
Programmers, being programmers, could not resist inventing a playful opposite for DRY. The result is WET, a lighthearted label that different people jokingly expand in different ways — “write everything twice,” “write every time,” or even “we enjoy typing” — all pointing at the same underlying habit: the same knowledge, copied and pasted into multiple places, left to quietly drift apart over time.
WET code is not usually the result of laziness. In fact, it is often the result of moving quickly under a real deadline. Copying an existing block of code and tweaking two lines is almost always faster, in the moment, than pausing to figure out the cleanest way to share that logic properly. The cost of that shortcut rarely shows up on the same day it was taken — it shows up weeks or months later, when a rule needs to change and someone has to go hunting through the whole codebase for every copy that needs updating too.
A bug report that says “we fixed this exact issue before” is one of the clearest signs of WET code hiding somewhere in a system — the fix was applied to one copy of the logic, while a forgotten sibling copy kept the old, broken behavior alive.
WET problems also tend to grow quietly worse over time, rather than staying fixed at whatever size they started. Once a rule has been copied twice, the natural, easy path for the next new requirement is often to copy it a third time, rather than pausing to reorganize something that “already works.” Each additional copy adds one more place a future fix must remember to visit, and the odds of a fix successfully reaching every single copy shrink a little more with each new one added. This is exactly why experienced engineers treat the appearance of a second or third copy of the same logic as a meaningful signal worth investigating, rather than a harmless coincidence to shrug off.
Three Kinds of Duplication
Not all repetition looks the same, and telling these varieties apart is one of the most useful skills this guide can offer. Broadly, duplication tends to show up in three recognizable shapes.
Learning to recognize which of the three shapes you are looking at matters more than it might seem, because each one tends to hide in a different place and calls for a slightly different response. Code duplication is usually the easiest to catch, simply by scanning a file. Logic duplication requires actually understanding what a piece of code is doing, not just what it looks like. Knowledge and data duplication is the hardest of all to spot, because it often lives outside of code entirely, spread across documents, spreadsheets, and separate systems that nobody thinks to compare side by side.
Code Duplication
This is the most visible kind: the exact same lines of code, or something extremely close to them, appearing in more than one place in a program. It is usually the easiest kind to spot, because it is literally sitting there on the screen, visible to anyone who looks.
Logic Duplication
This is subtler. Two pieces of code can look entirely different — different variable names, different structure, maybe even a different programming language — while still calculating the exact same underlying rule. A shipping discount calculated one way on the website and a slightly differently written, but logically identical, version calculated again inside a mobile app is a textbook example. Nothing about the two pieces of code looks alike, yet the same business knowledge is trapped in two separate places.
Knowledge and Data Duplication
This is the broadest and, in many ways, the most important kind. It covers facts stored more than once — a customer’s mailing address saved separately in three different systems, a tax rate written into both a spreadsheet and a piece of code, a company’s return policy explained slightly differently on a website page and inside a printed brochure. None of this is “code” in the traditional sense at all, yet it is exactly the kind of duplication Hunt and Thomas were most concerned about when they first wrote about DRY.
Usually Safe to Leave Alone
- Two things that look similar today but represent genuinely different facts.
- Short, simple code that is unlikely to ever need to change together.
- Coincidental similarity between unrelated parts of a system.
Usually Worth Fixing
- The same business rule, calculated in more than one place.
- The same fact, stored in more than one system, expected to always match.
- Copy-pasted code that has already needed the same bug fix twice.
The Building Blocks, Side by Side
A picture makes the difference between WET and DRY much easier to feel. Below, the left side shows a small business rule — a discount calculation — copied into three separate parts of an application. The right side shows the same rule, extracted once, with everything else simply pointing back to it.
Notice that the right-hand side is not really about writing less code overall — there is still a DiscountRules class, and there are still three separate parts of the application using it. The difference is entirely about where the actual decision-making logic lives. On the left, the decision is made three separate times, in three separate places, and all three copies must be remembered and kept in sync by hand. On the right, the decision is made exactly once, and everything else simply asks that one place for the answer.
Why DRY Matters So Much
It is worth being concrete about what DRY actually buys a team, because the benefits are easy to state abstractly but far more convincing once you see them spelled out plainly.
One truth, everywhere
When a rule lives in exactly one place, every part of the system that uses it automatically agrees with every other part, all the time, without anyone needing to double-check.
Fix it once, fixed everywhere
A bug fix or policy update applied to the one true source instantly applies wherever that source is used, instead of requiring a manual hunt through the whole codebase.
Less to memorize
A new team member only needs to learn where a rule lives once, rather than discovering, painfully, that three slightly different versions exist scattered around the system.
Less to read and maintain
Removing duplicated logic usually shrinks the overall size of a codebase, which directly reduces how much any single person has to read and understand.
There is also a quieter, more emotional benefit worth naming honestly: duplicated logic creates a background hum of anxiety for a team, even when nobody says it out loud. Every engineer who has been burned once by fixing a bug in the “wrong” copy of some duplicated logic starts double-checking, second-guessing, and moving more cautiously around that part of the system forever afterward. DRY code earns back a measure of trust — when you fix something, you can actually believe it is fixed, everywhere, the first time.
This trust compounds in ways that are easy to underestimate from the outside. A team working in a genuinely DRY codebase tends to move faster over time, not just at the moment a particular fix is made, because engineers spend less mental energy wondering whether they have found every copy of something and more energy actually solving the problem in front of them. A team working in a heavily WET codebase tends to slow down over time instead, as the accumulated weight of hidden, scattered duplicates makes every change feel riskier than it should.
Every piece of knowledge deserves exactly one true home.
A Worked Example: Feeling the Difference
Let’s walk through a small, realistic scenario twice — once the WET way, and once the DRY way — so the difference stops being an abstract idea and becomes something you can actually feel while reading code.
The WET Version
The same tax rule, copied into three placesclass WebCheckout {
double total(double price) {
double tax = price * 0.08; // 8% sales tax
return price + tax;
}
}
class MobileCheckout {
double total(double price) {
double tax = price * 0.08; // same 8% sales tax, retyped
return price + tax;
}
}
class RefundCalculator {
double refundAmount(double price) {
double tax = price * 0.08; // same rate, hidden inside a refund flow
return price + tax;
}
}
// Tax law changes to 9%. Someone must now find and fix THREE copies -
// and hope nobody forgets one.
The DRY Version
The same rule, given exactly one homeclass TaxPolicy {
static final double SALES_TAX_RATE = 0.08;
static double applyTax(double price) {
return price + (price * SALES_TAX_RATE);
}
}
class WebCheckout {
double total(double price) { return TaxPolicy.applyTax(price); }
}
class MobileCheckout {
double total(double price) { return TaxPolicy.applyTax(price); }
}
class RefundCalculator {
double refundAmount(double price) { return TaxPolicy.applyTax(price); }
}
// Tax law changes to 9%. Exactly ONE line, inside TaxPolicy, needs to change.
// Every caller updates automatically.
Notice what actually changed structurally: three classes still exist, and each still calculates a total. What moved is the actual decision — the tax rate and the formula for applying it — which now lives in exactly one place, TaxPolicy. Every caller trusts that one place to know the current, correct rule, rather than each keeping its own private, aging copy.
This is precisely the scenario Hunt and Thomas illustrated in their original writing about DRY — a financial rule scattered across multiple departments, each keeping its own copy, none of them fully in sync with the others.
It is worth pointing out something easy to miss in the DRY version: the three calling classes did not become more complicated, they became simpler. Each one now has exactly one job — describing how its particular checkout flow works — and none of them needs to know or care what the current tax rate actually is, or how it is calculated. That responsibility now belongs entirely to TaxPolicy. This is a small, quiet example of a much larger truth explored throughout this guide: removing duplication, done well, tends to make individual pieces of a system simpler to read, not more complicated.
Techniques for Staying DRY
DRY is a goal, not a single technique — different situations call for different tools to reach it. Here are the ones you will reach for most often.
None of these techniques are new inventions created specifically for DRY. They are simply the everyday tools of good software design, viewed through a particular lens: does this tool give a piece of knowledge exactly one clear, findable home? Recognizing that connection is often more valuable than memorizing the list itself, because it means every new tool you learn in the future can be evaluated through the same simple question.
Functions and Methods
The simplest, most common tool of all. Any time the same steps are repeated, wrapping them in a function with a clear name gives that logic exactly one home, and turns every other use of it into a simple call.
Constants
Numbers and text that mean something specific — a tax rate, a maximum file size, a company name — deserve a single, named constant rather than being typed out repeatedly as a “magic number” scattered through the code.
Shared Classes and Modules
When a whole cluster of related rules belongs together, grouping them into a dedicated class or module, as with the TaxPolicy example above, gives a whole family of related knowledge one clear, discoverable home.
Inheritance and Composition
When several related types share genuine common behavior, that shared behavior can be pulled into one place — a shared parent class, or a small object that gets reused across several types — rather than being rewritten separately for each one.
Templates and Includes
In documents, web pages, and configuration files, template systems let a shared header, footer, or block of repeated structure be written once and reused everywhere, rather than copy-pasted into every single file.
Configuration Files
Settings that might need to change — server addresses, feature toggles, pricing tiers — belong in one configuration source that the whole system reads from, rather than being hard-coded separately into multiple parts of a program.
Database Normalization
In database design, organizing data so each fact is stored in exactly one table, referenced by other tables rather than copied into them, is DRY applied directly to data rather than to code.
Whatever the tool — a function, a class, a config file, or a database table — the underlying move is always the same: find the one true home for a fact, and have everything else politely ask that home for the answer.
It is worth noticing that every technique on this list involves a small, upfront trade: a tiny bit of extra structure — a new function, a new class, a new shared file — in exchange for removing a duplicate. This trade is almost always worthwhile when the duplicated thing is genuinely shared knowledge. It is worth pausing over when the “duplication” being removed was actually quite small and simple to begin with, since the extra structure added to remove it can sometimes cost more, in ongoing complexity, than the tiny duplication it replaced.
DRY Beyond Code
Because DRY was originally described in terms of “knowledge” rather than just “code,” its reach naturally extends well past the source files a programmer types into every day.
One README, not five
Setup instructions explained once in a single, well-maintained document save a team from five slightly different, slowly diverging copies scattered across wikis, emails, and old chat threads.
Shared test setup
Automated tests that each rebuild the same sample data from scratch duplicate knowledge about what “a typical customer” looks like — a shared test helper keeps that definition in one place instead.
One button style, not many
A shared design system, defining what a button or a color looks like exactly once, keeps an entire product visually consistent instead of drifting screen by screen.
Reusable deployment scripts
Modern teams increasingly write their server setup itself as reusable, shared code, so a hosting environment is defined once and reliably rebuilt the same way every time.
This broader view also explains why DRY spread so quickly beyond just programming circles. Web development communities embraced it early as a guiding philosophy for building frameworks that automatically kept related pieces of a project in sync, and the underlying instinct has since made its way into data analytics, infrastructure automation, and everyday project documentation, wherever teams share knowledge that needs to stay consistent over time.
A particularly clear modern example comes from data and analytics teams, who often define important business measurements — how revenue is calculated, what counts as an active customer — inside shared, reusable definitions rather than letting every report or dashboard calculate the same figure slightly differently. When two dashboards disagree about a single number that supposedly measures the same thing, the root cause is almost always this same, familiar problem: the same knowledge was allowed to live in two separate places, and those places quietly drifted apart.
The Rule of Three
A common, practical question follows naturally from everything above: exactly how much repetition is too much? Seeing the same three lines of code appear twice does not necessarily demand an immediate rewrite. A widely shared piece of folk wisdom among experienced engineers offers a simple, memorable answer, often called the Rule of Three.
The first time you write a piece of logic, just write it. The second time you find yourself needing something very similar, it is reasonable to simply copy it again and keep moving — two copies are usually still cheap to manage, and it may still be unclear whether they truly represent the same underlying fact or just a coincidental resemblance. It is only the third time the same logic is needed that the balance of evidence usually tips firmly toward extracting it into one shared, reusable place.
First occurrence
Write the logic directly. There is nothing yet to compare it against, and no way to know if it will ever be needed again.
Second occurrence
A little repetition appears. It is often still too early to be confident about the right shared shape — copying is usually fine for now.
Third occurrence
A real pattern has emerged. This is usually the right moment to extract the shared logic into one place, since you now have enough real examples to design a sensible shared shape.
The wisdom behind this rule is not really about the exact number three — it is about patience. Extracting shared logic too early, based on only one or two examples, often means guessing at a shape that does not actually fit once a third, genuinely different case shows up. Waiting for a third real example gives you enough evidence to design a shared abstraction that actually earns its keep, rather than one built on a hopeful guess.
It also helps to notice what the Rule of Three is quietly protecting you from: the natural human urge to feel clever by spotting a pattern early and immediately generalizing it. That instinct is not wrong to have — noticing patterns is a genuinely valuable skill — but acting on it too quickly, before the pattern has proven itself across enough real examples, is where the trouble tends to start. Patience, in this specific sense, is not laziness. It is discipline.
When DRY Goes Wrong: Over-Abstraction
DRY is powerful advice, and like most powerful advice, it can be taken too far. The most common way this happens is a pattern sometimes called over-abstraction: an engineer notices two pieces of code that look similar, merges them into one shared function far too early, and ends up with a single, tangled piece of logic trying to serve two, three, or four genuinely different purposes at once.
This is, in a real sense, the mirror image of everything discussed so far in this guide. Where WET code fails by scattering one true fact across many places, over-abstraction fails by forcing several genuinely different facts to share one place that was never built to hold all of them comfortably. Both are, in their own way, a mismatch between the shape of the code and the shape of the real underlying knowledge it is meant to represent.
This overcorrection tends to produce code that is technically “DRY” by a shallow, surface reading — there really is only one copy of the logic — while actually being harder to understand, harder to change safely, and more fragile than the original, honestly duplicated version ever was. A shared function riddled with conditional branches, each one only relevant to one particular caller, is often a worse outcome than two small, separate, easy-to-read functions ever would have been.
A shared function that has grown a long list of boolean flags or special-case parameters, each one only used by a single caller. That pattern is a strong sign that two or more genuinely different pieces of knowledge were merged together too early, purely in the name of avoiding duplication.
There is a well-known, half-joking counter-principle that has emerged specifically as a reaction to this exact failure mode, sometimes shortened to AHA — “avoid hasty abstractions.” Its underlying advice fits neatly alongside the Rule of Three discussed above: a little duplication, left alone for a while, is often a smaller cost than a rushed, poorly fitting shared abstraction that everyone then has to work around for years afterward.
It is worth being honest that over-abstraction can be just as expensive as WET code, sometimes more so, precisely because it hides behind the appearance of good practice. A pile of duplicated code at least announces itself honestly — anyone reading it can see the repetition plainly. A tangled, over-generalized shared function often looks, at first glance, like a sign of careful, disciplined engineering, which makes it easy for a team to overlook the real cost it is quietly imposing on every single change that has to navigate its accumulated special cases.
The “Wrong Abstraction” Trap
A related, particularly sneaky trap deserves its own dedicated attention, because it explains why over-abstraction is so easy to fall into with the very best of intentions. Two pieces of code can look nearly identical today and still, quietly, represent two genuinely different ideas that simply have not yet had a reason to diverge.
Picture two features — sending a welcome email to a new customer, and sending a receipt email after a purchase — that both currently format an email in exactly the same way. Merging them into one shared sendEmail function feels like an obvious DRY win on day one. Then, weeks later, the marketing team wants welcome emails to include a personalized discount code, while the finance team insists receipt emails must never include any promotional content at all, for compliance reasons. The single shared function must now be pulled apart, carefully, back into two separate pieces — a more difficult and riskier operation than if the two had simply been left as separate, honestly duplicated functions in the first place.
A wrong abstraction is code that looks identical today for reasons that have nothing to do with actually being the same idea. Two things can coincidentally look alike without truly being alike — and only time reveals which is which.
The practical lesson many experienced engineers draw from this trap, often summarized in a well-known piece of programming folk wisdom, is that a slightly wrong, honestly duplicated piece of code is usually cheaper to fix later than a wrong shared abstraction, because untangling a bad abstraction after the fact tends to be more disruptive than simply extracting a good one a little later than you might have liked.
A practical technique for reducing this risk is to pay close attention to why two pieces of code look the same, not just that they look the same. If the similarity comes from a shared underlying business concept — both are calculating the same tax, both are validating the same email format — that is a strong candidate for genuine shared knowledge. If the similarity comes purely from coincidence — two features that happen to both loop over a list and print something, for entirely unrelated reasons — that resemblance is much shakier ground to build a shared abstraction on top of.
DRY and Its Neighboring Principles
DRY rarely operates alone — it sits alongside several other well-known design ideas, and understanding how they relate helps prevent DRY from being applied in a way that quietly works against the very qualities it is meant to protect.
It helps to think of these principles less as separate, competing rules and more as different lenses, each highlighting a different risk in the same underlying decision. DRY asks “is this knowledge duplicated?” KISS asks “is this still easy to follow?” Single responsibility asks “does this piece of code have one clear job?” A genuinely good design decision usually satisfies all three lenses at once — and when a proposed change only satisfies one of them while clearly failing the others, that mismatch is worth pausing over.
DRY and KISS
KISS — “keep it simple” — is a close, friendly neighbor of DRY, but the two can occasionally pull in different directions. A determined effort to remove every trace of duplication can sometimes produce a more complicated, harder-to-follow system than a little honest repetition would have. When the two principles conflict, simplicity is usually the safer choice to favor.
DRY and the Single Responsibility Principle
The idea that a class or function should have exactly one reason to change complements DRY nicely: a shared piece of logic extracted to satisfy DRY should also have one clear, focused job, rather than becoming a dumping ground for every vaguely related piece of behavior in the system.
DRY and Composition
Many of the same techniques that help keep class hierarchies shallow and flexible — small, focused, reusable helper objects — are also excellent tools for satisfying DRY, since a well-designed helper object is, by definition, one authoritative home for a specific piece of behavior.
When These Principles Reinforce Each Other
- A shared function is both DRY and genuinely simple to read.
- A helper object has one clear job and one clear reason to change.
- Removing duplication also removes unnecessary complexity.
When They Pull in Different Directions
- A “DRY” abstraction that only a few people fully understand.
- Merging logic that looks similar but serves genuinely different purposes.
- Chasing zero duplication at the cost of straightforward, obvious code.
A Spot-and-Fix Recipe
When you do find duplication worth addressing, here is a calm, repeatable way to handle it thoughtfully rather than reflexively.
Notice that this recipe deliberately places judgment before action. It would be easy to write a much shorter version of this recipe — “find duplication, extract it” — but that shorter version is exactly what leads teams into the over-abstraction and wrong-abstraction traps discussed earlier. The extra steps here, asking whether something is truly shared knowledge and checking against the Rule of Three, are not bureaucratic overhead. They are the difference between removing duplication that was genuinely hurting the system and creating a new, more subtle problem in its place.
Notice the repetition
Two pieces of code, two documents, or two stored facts that look alike or say the same thing.
Ask if it is knowledge or coincidence
Do these two things represent the exact same underlying fact or rule, or do they just happen to look similar right now?
Check the Rule of Three
Is this the first, second, or third time this pattern has appeared? Patience is often the right answer for the first two.
Give it exactly one honest home
If it truly is repeated knowledge, extract it into a clearly named function, class, constant, or shared document.
Update every caller
Point every place that used the old, duplicated version toward the new, single, authoritative source instead.
Watch for future divergence
If two of the merged callers later need genuinely different behavior, be willing to split the shared logic back apart rather than forcing it to serve everyone with special-case flags.
Step six is the one most guides forget to mention, and it matters just as much as the others. DRY is not a one-time cleanup — it is an ongoing judgment call that sometimes needs to be revisited and reversed as a system’s real requirements become clearer over time.
Side-by-Side Comparison
The table below summarizes the practical differences between code that has embraced DRY thoughtfully and code that has drifted into either WET repetition or hasty over-abstraction.
| Aspect | WET Code | Thoughtful DRY Code |
|---|---|---|
| Same rule changes | Must be found and fixed in every copy | Fixed once, applies everywhere automatically |
| Risk of inconsistency | High — copies quietly drift apart | Low — one authoritative source |
| Readability of any one spot | Simple locally, risky system-wide | Clear, provided the abstraction fits well |
| Onboarding a new engineer | Must discover every hidden copy | Learn the one true source and trust it |
| Risk of a bad shared abstraction | Not applicable — nothing was shared | Real risk if extracted too early |
| Best suited for | Genuinely one-off, unlikely-to-repeat code | Facts and rules that must always agree |
The table deliberately avoids declaring one column simply “good” and the other simply “bad.” A small amount of WET code, deliberately left alone because a pattern has not yet proven itself, is a perfectly healthy, temporary state for a codebase to be in. The goal is not to eliminate every entry from the left column immediately — it is to make sure nothing sits there permanently, unnoticed, once it has clearly become the kind of repeated knowledge this guide has spent so much time describing.
Common Pitfalls
Beyond the two major traps already covered in detail — over-abstraction and the wrong abstraction — a handful of smaller, everyday habits tend to trip teams up again and again.
Chasing Zero Duplication as a Score
Some teams start treating “amount of duplication” as a number to minimize for its own sake, rather than a signal to interpret thoughtfully. This mindset tends to produce exactly the kind of hasty, poorly fitting abstractions discussed earlier, purely to make a metric look better.
Confusing Similar-Looking Code With Shared Knowledge
Two functions that happen to be five lines long and structurally similar are not automatically duplicating knowledge. This is the single most common source of the “wrong abstraction” trap described earlier in this guide.
Forgetting DRY Applies to More Than Code
Teams that diligently avoid duplicated code while still maintaining three separate, slowly diverging copies of their setup documentation have only solved half the problem DRY was originally meant to address.
Refusing to Un-DRY Code When Requirements Diverge
Once two callers of a shared function genuinely need different behavior, clinging to the shared function out of a stubborn commitment to “staying DRY” usually produces worse code than simply splitting it back into two clear, separate pieces.
A shared function whose name has grown vague and generic over time — process, handle, doStuff — because it has quietly absorbed several unrelated responsibilities in the name of avoiding duplication. A shrinking, less specific name is often the clearest early warning sign of this pattern.
A Few More Real-World Analogies
One master recipe card
A restaurant keeps one official recipe card for a dish, not five slightly different versions in each cook’s head — so the dish tastes the same no matter who is cooking that night.
Defined terms, used once
Contracts formally define a term once near the top — “the Tenant,” “the Property” — and reuse that exact defined term everywhere else, rather than re-explaining it in slightly different words each time.
One house rule, not five versions
A family that agrees on one clear bedtime rule, told the same way to every child, avoids the confusion of each parent remembering and enforcing a slightly different version.
One official map, many uses
A city publishes one authoritative map. Tour guides, delivery apps, and emergency services all reference that same map rather than each drawing their own slightly different version.
Think about a single light switch controlling a room, versus five separate switches, each wired to control the same light independently. With one switch, everyone in the house instantly and reliably agrees on whether the light is on. With five separate switches all wired to the same bulb, it becomes far too easy for someone to think the light is off, based on their own switch, while it is actually still on because of another. DRY software works like the single switch — one clear, reliable source of truth that everyone checks, rather than five separate, potentially disagreeing copies of the same information.
What ties every one of these analogies together is a simple pattern worth carrying forward: whenever a fact matters enough that people need to trust it and act on it consistently, that fact deserves exactly one carefully maintained home. The moment the same fact is allowed to exist in two places at once, someone, somewhere, is quietly relying on the possibility that both copies still agree — and sooner or later, that quiet assumption gets tested.
Frequently Asked Questions
Does DRY mean I should never copy and paste code?
No. A small amount of copying, especially early on or for genuinely unrelated pieces of logic that only look similar by coincidence, is often perfectly fine. DRY is about repeated knowledge, not about the literal act of copying text.
How do I know if two similar pieces of code represent the same knowledge?
Ask whether a future change to the underlying business rule or fact would need to update both places together, every time, without exception. If yes, it is likely the same knowledge. If the two could reasonably change independently for unrelated reasons, they are probably not.
Is the Rule of Three a strict law?
No, it is a widely shared piece of practical folk wisdom, not a strict rule. It exists mainly to counter the temptation to abstract too early, based on too little evidence about the true shared shape of a piece of logic.
Can DRY apply to teams and processes, not just code?
Yes. Many teams apply the same underlying idea to meetings, documentation, and workflows — keeping one authoritative project plan, for example, rather than several separately updated copies scattered across different tools.
What should I do if I inherit a codebase full of duplication?
Resist the urge to fix everything at once. Apply the spot-and-fix recipe from this guide gradually, prioritizing the duplication that has already caused real bugs or confusion, rather than attempting a single sweeping rewrite.
Is DRY still relevant today, more than two decades after it was introduced?
Very much so. It remains one of the most frequently cited principles in professional software discussions, and the underlying discipline of managing shared knowledge carefully has, if anything, become more relevant as systems and teams have grown larger and more interconnected.
Does DRY conflict with writing readable, self-contained code?
It can, if applied without judgment. A single, deeply shared function used in a dozen unrelated places can become harder to read than several small, separate, self-contained ones. When DRY and readability pull in different directions, favoring the version a new teammate could understand fastest is usually the wiser choice.
How is DRY different from simply reusing code?
Reuse is one common way to achieve DRY, but the two are not identical. Code can be technically reused while still representing coincidentally similar, unrelated knowledge — which is exactly the wrong-abstraction trap this guide has warned about. DRY is about the underlying knowledge staying singular; reuse is just one of the tools that can help make that happen.
Glossary
A short reference of the terms used throughout this guide, useful to revisit whenever a conversation with your own team turns to duplication and shared knowledge.
| Term | Definition |
|---|---|
| DRY | “Don’t Repeat Yourself” — the principle that every piece of knowledge should have exactly one authoritative home in a system. |
| WET | A playful backronym describing the opposite of DRY — the same knowledge scattered across multiple, separately maintained copies. |
| Single Source of Truth | The one authoritative place where a particular fact or rule officially lives, which everything else refers back to. |
| Code Duplication | The same or nearly identical lines of code appearing in more than one place. |
| Logic Duplication | The same underlying rule or calculation expressed in different-looking code in more than one place. |
| Rule of Three | The practical guideline of waiting for a third occurrence of similar logic before extracting it into a shared abstraction. |
| Over-Abstraction | Merging code too early or too aggressively in the name of DRY, producing a shared piece of logic that is harder to understand than the duplication it replaced. |
| Wrong Abstraction | Code that looks similar today by coincidence rather than shared meaning, which causes trouble once it is merged and later needs to diverge. |
| Normalization | A database design technique that organizes data to avoid storing the same fact in more than one place. |
Key Takeaways
This guide has covered a wide arc, from a simple story about a misplaced home address to the subtle traps hiding inside well-intentioned refactoring decisions. The list below distills everything into the ideas most worth carrying into your own daily work.
Remember This
- DRY stands for “Don’t Repeat Yourself,” and it means every piece of knowledge should have exactly one authoritative home in a system.
- DRY was named by Andy Hunt and Dave Thomas in their 1999 book, though the underlying instinct traces back to earlier ideas like database normalization.
- DRY is about repeated knowledge, not repeated text — two similar-looking blocks of code may or may not represent the same underlying fact.
- WET code — the same knowledge copied into multiple places — creates hidden risk, since every copy must be found and updated together whenever the rule changes.
- The Rule of Three suggests waiting for a third genuine occurrence of a pattern before extracting a shared abstraction, to avoid guessing at the wrong shape too early.
- Over-abstraction and the “wrong abstraction” trap are real risks of applying DRY too eagerly, and can produce code that is harder to work with than honest duplication.
- DRY reaches beyond code into documentation, tests, database design, and team processes — anywhere the same knowledge could be scattered across multiple places.
- DRY works best alongside neighboring ideas like simplicity and single responsibility, and it is fine, even healthy, to reverse a DRY decision once requirements genuinely diverge.