A food delivery app decides that delivery will now be free only above Rs 599 instead of Rs 499. A developer searches the code, finds the old number in the cart screen and the payment screen, changes both, and ships. Two days later support starts getting complaints: customers paid Rs 590 for a Rs 550 order, but the invoice in their email says Rs 550. The rule was also written inside the invoice code, and nobody remembered it was there.

Bugs like this have nothing to do with clever algorithms. They happen because the same piece of knowledge was written down in more than one place. The principle that guards against it is called DRY, short for Don't Repeat Yourself.

"Every piece of knowledge must have a single, unambiguous, authoritative representation within a system." (Andy Hunt and Dave Thomas, The Pragmatic Programmer)

Real-world analogy: a college puts the exam timetable on one notice board. If every class teacher copied it by hand into their own register, a single change of date would have to be corrected in twenty registers, and one of them would surely be missed. With one notice board, you fix it once and everyone reads the same thing.

1. What Is the DRY Principle?

DRY says that each rule, number or fact your program depends on should live in exactly one place. Every other part of the code that needs it should refer to that one place instead of keeping its own copy.

Notice that the definition talks about knowledge, not lines of code. Two lines can look identical and still mean different things, and one rule can be repeated in code that looks completely different. In a typical Java project the knowledge that tends to get copied is:

  • business rules, such as delivery fees, discounts, late fines or eligibility checks
  • magic numbers, such as a minimum password length, a tax rate or a page size
  • validation logic for email, phone numbers, PIN codes and so on
  • message text and formats that appear on several screens or receipts

When one of these exists in two places, the two copies will eventually disagree. DRY is simply a way of making sure that cannot happen.

2. A Problem You Can Run: One Rule, Three Copies

Here is the delivery app from the introduction. Cart, Checkout and Invoice each calculate the delivery fee themselves. The rule recently changed to "free above Rs 599", and the developer updated two of the three copies.

Output

Order of Rs 450 -> cart: 490, charged: 490, invoice: 490
Order of Rs 550 -> cart: 590, charged: 590, invoice: 550
Order of Rs 650 -> cart: 650, charged: 650, invoice: 650

For Rs 450 and Rs 650 everything looks fine, which is exactly why this kind of bug survives testing. Only an order between the old and the new limit shows the problem: the customer is charged Rs 590 but the invoice says Rs 550.

Nobody wrote "wrong" code here. Each class was correct on the day it was written. The design itself made the mistake easy, because the knowledge "when is delivery free?" had three owners.

3. Why Repetition Hurts

A little duplication feels harmless in a small project. It gets expensive as the code base and the team grow, for a few connected reasons.

  • Every change becomes a search. You have to find all the copies before you can change a rule, and nothing in the compiler tells you that you missed one.
  • Copies drift apart. Someone fixes a bug in one copy, someone else adds a check to another, and after a few months the copies quietly behave differently.
  • Bugs hide in the copy nobody tests. The cart is used all the time, so its copy is well tested. The invoice copy is only checked when someone reads an email.
  • Tests multiply. Three copies of a rule need three sets of tests, and in practice only one of them gets written.

A useful habit is to ask yourself before you copy and paste: if this rule changes next month, will I remember every place I pasted it? If the honest answer is "probably not", the code needs one home for it.

4. Applying DRY: Give the Rule One Home

The fix is to move the knowledge into a single class and let the other classes ask that class. In Java a small class with static final constants and static methods works well for a pure rule like this one. The name matters too: DeliveryFeePolicy tells a reader exactly where to look when the fee rule changes.

Output

Order of Rs 450 -> cart: 490, charged: 490, invoice: items 450 + delivery 40 = 490
Order of Rs 550 -> cart: 590, charged: 590, invoice: items 550 + delivery 40 = 590
Order of Rs 650 -> cart: 650, charged: 650, invoice: items 650 + delivery 0 = 650

All three totals now agree for every order. More importantly, the next time the business changes the limit, there is exactly one line to edit: FREE_DELIVERY_ABOVE. Cart, Checkout and Invoice do not even know what the limit is. They only know whom to ask.

Notice that Invoice still formats its own line of text. DRY does not mean every class must look the same. It means the rule is not repeated. How each screen presents the result is that screen's own business.

5. DRY Is About Knowledge, Not Just Copy-Paste

Duplication is not always a block of pasted code. Often it is the same fact written twice in slightly different ways. In the next example the sign-up form and the reset-password form both check password length, but someone typed 8 in one and 6 in the other. The code does not look copied at all, yet it is the same knowledge stored twice.

Output

Before (two copies):
  abc123 -> signup: rejected (needs 8+ characters), reset: accepted
  sunflower -> signup: accepted, reset: accepted
  sunflower7 -> signup: accepted, reset: accepted
After (one PasswordPolicy):
  abc123 -> signup: rejected (needs 8+ characters), reset: rejected (needs 8+ characters)
  sunflower -> signup: rejected (needs a digit), reset: rejected (needs a digit)
  sunflower7 -> signup: accepted, reset: accepted

Before the change, a user can reset their password to abc123 even though they could never have signed up with it. After moving the rule into PasswordPolicy, both forms give the same answer. There is a second benefit hidden in the output: when the team decided passwords also need a digit, that new check was added in one method and both forms picked it up immediately.

A good sign that you have this kind of duplication is a number with no name. If you see 8, 18 or 0.18 sitting in the middle of an if, ask whether the same number appears elsewhere. Giving it a name like MIN_LENGTH or GST_RATE in one class is often the first and easiest DRY fix.

6. When Not to Merge: Accidental Duplication

DRY is a guideline, not a law that every pair of similar lines must be merged. Sometimes two pieces of code look identical today but represent two different rules that just happen to match. Merging them ties together things that should be free to change on their own.

In this example the library fine and the hostel late-entry fine were both Rs 10 per day, so someone replaced them with one SharedFine. Then the library lowered its fine to Rs 5.

Output

Merged too early:
  library, 4 days late: Rs 20
  hostel, 3 late nights: Rs 15  <- hostel fine changed by accident
Kept separate:
  library, 4 days late: Rs 20
  hostel, 3 late nights: Rs 30

Changing the shared number for the library silently halved the hostel fine as well. The two rules were owned by different people for different reasons, so they were never really "the same knowledge". Keeping LibraryFine and HostelFine separate is the correct design even though the code looks repetitive.

Two rules of thumb help here:

  • Ask whether the copies must change together. If changing one should always change the other, they are one piece of knowledge. If not, leave them apart.
  • Use the Rule of Three. The first time you write something, just write it. The second time, notice the repetition but tolerate it. By the third time you usually understand the pattern well enough to extract it correctly.

Sandi Metz put it well: "duplication is far cheaper than the wrong abstraction". A wrong shared method is harder to undo than two similar methods are to merge later.

A few more places where a little repetition is fine:

  • Unit tests. A test should read from top to bottom without jumping to five helper methods. Creating the same test object in two tests is usually clearer than hiding it in a shared factory.
  • Trivial code. Writing MathUtils.addOne(x) to avoid typing x + 1 twice adds a lookup and saves nothing.
  • Code owned by different teams or services. Sharing a helper between two independently deployed services couples their release cycles. Sometimes a small copy is the lesser evil.

7. Common Ways to Remove Duplication in Java

  • Extract a method when the same few lines appear inside one class.
  • Introduce a named constant (static final) for a number or string used in more than one place.
  • Use an enum when the same set of values and their properties, such as order states or membership levels, is checked in many places.
  • Create a small policy or validator class, like DeliveryFeePolicy or PasswordPolicy, for a business rule used by several classes.
  • Use composition or a shared base class when several classes need the same behaviour. Prefer composition unless there is a real "is-a" relationship.
  • Move values to configuration (for example application.properties in Spring Boot) when they change per environment, such as URLs or timeouts.

8. DRY and Other Principles

DRY works best together with the principles around it. KISS stops you from building a complicated framework just to avoid two similar lines. YAGNI stops you from creating a shared helper for a second use case that does not exist yet. The Single Responsibility Principle pairs naturally with DRY: when a rule has one home, that home also has one clear reason to change.

9. Common Mistakes

  • Fixing a bug in one copy of a rule and assuming the other copies are fine.
  • Merging code that only looks similar, like the library and hostel fines, and creating hidden coupling.
  • Building a big CommonUtils class that holds every shared method in the project. Shared code still needs a sensible home and a clear name.
  • Leaving unnamed magic numbers in conditions, which makes duplicated rules hard to spot.
  • Over-applying DRY in tests until nobody can read a failing test without opening four helper classes.

Summary

  • DRY means every piece of knowledge, such as a rule, a number or a format, has one authoritative home in the code.
  • Duplicated knowledge leads to copies that drift apart, bugs that hide in the least-tested copy, and changes that need a search across the project.
  • In Java, the usual fix is a named constant, an enum, or a small policy class that other classes call.
  • Only merge code that represents the same knowledge and must change together. Similar-looking code with different owners should stay separate.
  • Follow the Rule of Three, and remember that a little duplication is cheaper than the wrong abstraction.

Next time you reach for copy and paste, pause for a second and ask where this knowledge should live. That one question prevents most of the bugs in this article.