Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Quite simple, why would I want to write code that works for all cases and scalable data when all I need to do is repeat the same process a few times with a few minor tweaks?

I'm unlikely to need to edit this again any time soon.

It looks like a lot of less work to just go...

function doStuff1(){/*.a.*/}
function doStuff2(){/*.b.*/}
function doStuff3(){/*.c.*/}

And if I ever need to add something...

function doStuff4(){/*.d.*/}

And if I need to remove it, I remove it.

It's harder to figure out how to make all of those into one straight-forward pattern that I can just feed data into and deal with all the cases, and make a bunch of changes I don't feel like I'm ever going to have to do.

Why be DRY when it looks like a quick cut+paste is going to be so much less work?

share|improve this question
10  
because dry is still faster when you do it right, also what if you made a mistake in a. that effects all the others –  Daniel Little Aug 24 '11 at 1:54
85  
"I'm unlikely to need to edit this again any time soon" — you may hope, but most probably you're making a mistake here. And if you're going to work on that code again, but not so soon, it will only make things worse; you will forget where duplicates are, and duplicates will grow subtle but treacherous discrepancies. "Write as if the person who will maintain your code is a dangerous maniac that knows where you live", to quote the classics. –  9000 Aug 24 '11 at 2:06
14  
I think you can pretty much sum it up with: A single point of change is easier to maintain. –  Falcon Aug 24 '11 at 7:08
16  
If you can't answer this yourself, you need to get some more real world experience of development and maintenence. –  David Heffernan Aug 24 '11 at 9:57
9  
@Wayne I felt a great disturbance in the source, as if millions of programmers suddenly cried out in terror. –  Incognito Aug 24 '11 at 13:43

14 Answers 14

up vote 105 down vote accepted

If you repeat yourself, you can create maintainability issues. If doStuff1-3 all have similarly structured code and you fix a problem in one, you could easily forget to fix the problem in other places. Also, if you have to add a new case to handle, you can simply pass different parameters into one function rather than copy-pasting all over the place.

However, DRY is often taken to an extreme by clever programmers. Sometimes to not repeat yourself you have to create abstractions so obtuse that your teammates cannot follow them. Sometimes the structure of two things is only vaguely similar but different enough. If doStuff1-4 are different enough such that refactoring them to not repeat yourself causes you to have to write unnatural code or undergo clever coding backflips that will cause your team to glare at you, then it may be ok to repeat yourself. I've bent over backwards to not repeat myself a couple of times in unnatural ways and regretted the end product.

I always err on the side of DRY, in the rare case repeating myself when I think that the benefits in readability are worth the risks of someone forgetting to fix a bug in multiple places.

Taking that advice into account, it sounds like in your case

repeat the same process a few times with a few minor tweaks

I would definitely work hard to not repeat myself in your case. Assuming minimal "tweaks" -- they can be handled with different parameters that impact the behavior or perhaps dependancy-injected to perform different subtasks.

Why be DRY when it looks like a quick cut+paste is going to be so much less work?

Famous last words. You will regret thinking that when a junior engineer tweaks/fixes/refactors one doStuff and doesn't even realize the others exists. Hilarity ensues. No mostly heartburn ensues. Every line of code costs more. How many code paths must you test with so many repeated functions? If one function, you just have to test one main path with a few behavioral modifications. If copy-pasted you have to test every doStuff separately. Odds are you'll miss one and a customer may have an unwelcome bug and you may have some unwelcome emails in your inbox.

share|improve this answer
1  
Er, just to be clear, I'm not proposing dry is bad, this question is more of devils advocate. I'm really looking for a logical response I can link to people who think cut+paste+tweak code is fine. –  Incognito Aug 24 '11 at 2:21
2  
That being said, I like your answer the most as it covers both failures in DRY: not bothering, and going over-board, as well as explaining impacts. -- On the one point about sacrificing readability for bugs, I'd argue that repetitive code is less readable for the same reason you point out, where it's easy to lose track of things. –  Incognito Aug 24 '11 at 2:22
12  
I would underline one easy pitfall of DRY: similar code merged. If you have two use cases that are functionally unrelated but happen to have very similar code, it is easy to merge the two because DRY is good. Unfortunately, when one needs to evolves, you then often find yourself with the unpleasant task of having to split the function once again, and then go through all call sites and carefully consider which one should be called here... Example: LLVM structural typing (all similar types are merged into one) makes it nigh impossible to map the IR back to the original code. –  Matthieu M. Aug 24 '11 at 17:40

Because DRY will be less work later.


DRY: (Don't Repeat Yourself)

One function taking an argument.

def log(arg):
    print(arg)

C&P: (Copy&Paste)

26 gazillion functions doing essentially the same thing, but with a 2 char difference.

def logA():
    print('a')

def logB():
    print('b')

...ad infinitum...

How about we update our printing to specify what exactly is printing?

DRY:

def log(arg):
    print(arg + "Printed from process foo")

Done.

C&P:

You have to go back and change every single function.


Which do you think would be easier to debug?

share|improve this answer
8  
Also, you need to write as many similar tests suites as you have duplicate functions. –  9000 Aug 24 '11 at 2:08
    
You've illustrated the concept adequately, but in practice nobody would do what you described with the gazillion functions, not in that way anyway. –  Robert Harvey Aug 24 '11 at 2:38
    
@Robert I would hope not! I picked a very simple task to try to better illustrate the concept and why it can be a good thing. –  John Aug 24 '11 at 3:19
8  
@Robert - have you read any of the articles at thedailywtf.com ;) - there are some out there who would do just that –  HorusKol Aug 24 '11 at 7:33
1  
@9000 Not if you don't have any test suites :p (which actually often might be the case in some projects... unfortunately...) –  Svish Aug 24 '11 at 11:16

Since everyone else has done a great job at explaining the maintainability issues with duplicate code, I'll just say this:

Much of programming requires you to think about the future, not just the immediate present. You're right that copy & paste is easier now, but the statement, I'm unlikely to need to edit this again any time soon" shows you're not thinking correctly. Yes, you might buy yourself a bit of time with a quick and dirty copy/paste, but in doing so you're showing that you can't look beyond your immediate problem and think about tomorrow. Are you positive you'll never need to revisit this code? Do you know for certain there are no bugs in it? Can you 100% guarantee you won't need to revisit it when your next set of features need to be implemented? Those are issues for tomorrow, and need to be considered when you're designing today.

Of course, there are times when copy/paste will be necessary. As a UI developer, I've found there are times when I have to violate the DRY principle. It sucks, I cringe every time it happens, and thankfully, it's rare. But it does happen.

The difference is that when violating DRY, you should have a very compelling reason to do so, and the statement, It's harder to figure out how to make all of those into one straight-forward pattern isn't really one of them. Unless you are under a massive time crunch and have your boss screaming to get something in the next few hours or you'll lose your job, I don't think this is a valid rationale.

Don't take this the wrong way: I'm not trying to castigate or chastise you, but rather try and get you to see where your mentality is wrong. Programmers invest in future laziness; DRY is a way of achieving that. The work you do today solving a difficult design problem will payoff tomorrow.

share|improve this answer
    
I'm not sure that I agree with a boss telling you "get it done or you're fired" is a great reason to violate DRY. Technical debt, when do you have time to do it right, is it really time-saving, etc? –  Incognito Aug 24 '11 at 2:35
1  
@Incognito, I was trying to be a bit ironic :) –  bedwyr Aug 24 '11 at 2:38

Because, applied to your example:

  • + readability

    Less code often translates to less noise. (not always...)

  • + flexibility

    If you ever had to change the behavior of the doStuffX, you'll want to kill yourself or whoever wrote it,

  • + extensibility

    If you had extracted the distinct parts to a data-structure of your choice and then just iterated over it by calling a generic doStuff, you could just as well add one line in your data structure where you want a new entry, or remove one, and changing the behavior will just mean editing doStuff. Easier to maintain.

  • + cost efficiency

    less code here means:

    • => less development => reduced cost
    • => less probability for bugs => less support time => reduced cost
  • + (possible) managed optimization

    Depending on the language, the compiler/interpreter might have a bigger chance of determining that generic doStuff does always the nearly identical things often one call after another, and could inline it or attempt to optimize it. Probably wouldn't for X variations of doStuffX.

  • + testing and quality

    Testing is easier: doStuff needs testing, and that's it. Well, not exactly, but that covers already more. Only its IO expectations vary and need to be tested under different conditions, but it's still a lot easier to test and more maintainable than all the variations of doStuffX.

Overall this accounts for more maintainable code and an improved development efficiency for your team, and it is one of many good practices to help you produce more robust and dependable software.

share|improve this answer

I'm unlikely to need to edit this again any time soon.

If this is really truely the case, then you might be able to get away with it, but more often than not you're going to be working on code that needs to be maintained. That means extending functionality, fixing bugs and other improvements. If you have small variations of the same code in 10 different places, and one day you come back to that code and need to make a change, you now have the error prone task of making the same change in 10 different places (Sorry, there were 11 places, you forgot one and now you have a bug).

If you can generalize what problem you're trying to solve you can make your code easier to extend and fix if bugs pop up.

share|improve this answer
    
Cool answer, but even if I might get away with it, what about the poor sap that gets to maintain it after me? ;). –  Incognito Aug 24 '11 at 2:17
    
See: "more often than not you're going to be working on code that needs to be maintained" :) –  Alex Aug 24 '11 at 3:40
    
Even then it's only the case if what you're copying and pasting is 100% bug-free perfection. And it's not, and stop thinking it might be. –  Dan Ray Aug 24 '11 at 12:41
    
I will admit I've copied and pasted stuff in the past, but never for anything I was going to keep around for more than a day. Sometimes you just need a quick and dirty throw-away script. –  Alex Aug 24 '11 at 15:08

As I stated in the answer to another question, my approach is the following:

  1. The first time I solve a certain problem, I just get it done.
  2. The second time (i.e. when I solve a similar problem) I think: hm, maybe I am repeating myself, but I will go for a quick copy-and-paste for now.
  3. The third time I think: hm, I AM repeating myself -> make it general!

I.e. up to 2, another principle (YAGNI) wins over DRY. But starting from 3 (or 4 if I am really lazy!) it seems I AM gonna need it and so I follow DRY.

Update

Some further ideas from my recent experience. I had to adapt / integrate two components A and B developed by another team into our product. First: the two components A andb B are very similar to each other, so I was already disturbed by the fact that they had a somewhat different architecture. Second: I had to adapt them so I would have been glad to use subclasses and only override what I really needed.

So I started refactoring these two components (each of which consists of about 8 C++ classes): I wanted to have a common architecture for both A and B, and then add the features we need by defining subclasses. In this way, our two new components A' and B' would have been derived from the existing ones.

After two weeks trying to get a common and well-defined structure out of the existing code and having to explain during our daily meetings that I was making little progress because the original code was too messy, I spoke to my boss. We observed that we were not going to need anything more than these two new components A' and B' (there were not going to be four or six of them, just those two).

Ok, so be it: I did a massive copy and rename of classes from A and B and started to adapt the copy of the code. I got it to work in two more weeks (still doing some bug-fixing now).

Advantages: We have the functionality almost finished now and when we have fixed all the bugs we are finished. We have saved all the refactoring and testing of A and B.

Disadvantages: Two weeks ago the other team changed another component C, which is used by A and B. They adapted A and B but A' and B' were also broken and we had to change them ourselves. This introduced a new bug that we had to fix. This extra work would probably have been unnecessary if A' and B' had shared most of their code with A and B.

So: code duplication is always dangerous. I think it is always a matter of finding trade-offs and often it is not easy.

share|improve this answer
    
+1 for mentioning that YAGNI can balance DRY. –  Donal Fellows Aug 24 '11 at 17:06

Just to clarify, as I don't find this in any of the other answers:

DRY is a principle of software development aimed at reducing repetition of information of all kinds.

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

The DRY principle as mentioned by Andy Hunt and Dave Thomas isn't limited to preventing duplication of code. It also advocates code generation and any automation processes. Ironically, the results of code generation could even be duplicate code ...

The reason why has already been explained thoroughly in the other answers, but Falcon's comment sums it up well enough IMHO:

A single point of change is easier to maintain.

share|improve this answer
    
Oh wow, I thought the tag had some data in it. I'll put some info in there. –  Incognito Aug 24 '11 at 13:03

I have to mention the problems with DRY in the relational database world. Databases are designed to perform quickly and well using set-based logic and through sargable queries. DRY principles often cause the developer to write non-Sargable queries or use Row-by-agonizing-Row logic to leverage existing code in multiple situations. DRY and performance optimization are often at odds and in the database world, performance is usually far more critical than maintainabilty. This doesn't mean that you should not use DRY principles at all, just that you should be aware of how it will affect the overall usability of the database. Application developers thing DRY first and performance second, database developers think data integrity first, performance second,security of the data third(performance and security might swap places in some systems). Maintainibilty and thus DRY is a far distant 4th.

I've noticed in general, that the more layers of abstraction you put into database queries the slower they become. I'm not saying I didn't wish the people who design the datbase programs themselves didn't do a better job of allowing developers to use DRY without affecting how well the database performs, but I don't design database software at that level, so perhaps the conflict between abstraction and performance in database is harder to fix than I suppose. However, we have to work with the systems as they are currently built. We can ask for better implementation of DRY principles in future releases that won't also kill performance (and it has gotten better through the years but is still problematic), but in the meantime we must consider if DRY is the right move for this database at this time.

But often the very features that you want to use to ensure the DRY principle is met are the ones that cause tremendous problems for the database. I'm not saying never use DRY but don't go overboard with it.

Examples of what I'm talking about. You need to do a data import of a million records once a month. Records can already be manually added through the user interface calling a stored proc. This proc, because it was designed for single record imports, only adds one record at a time. Using DRY to avoid having the insert code in two places, you write a cursor to call the proc repeatedly rather than write the set-based imports you need. Time for the import goes from the 30 minutes it would take using set-based logic to 18 hours. Now the right way to adhere to DRY in this case would be to fix the proc to handle mulitple record imports. Unfortunately, it is often impossible or very difficult to send an array to a proc (depending on the db back end) and by changing the proc, you end up breaking the application.

Scalar functions and table-valued functions are also used to implement DRY principles and again they can seriously affect performance especially if you need to use them in a way that prevents the indexes from being useful.

Views are also good for implementing DRY. However, if you implement DRY through the use of views that call views that call other views, you will quickly get to the point where the queries will timeout under load. In fact you might end up needing to generate data sets of millions of records when you only need three at the end. So a one-level view of a complex set of joins to implement DRY can be excellent (I have one myself that we use to make sure all financial reporting uses the same base set of tables and calculations of certain things), more than two levels and you need to consider if you are creating a performance mess.

share|improve this answer

There is such a thing as too much DRY. When this happens, two concepts which appear at some point to be similar enough to warrant factoring code (1) may later turn out to be different enough that they deserve separate implementations.

In other words, DRY and loose coupling sometimes conflict. If you expect doStuff1 and friends to diverge with every new release of the software, it's OK to duplicate their code.

In my experience, it can be difficult to judge where your software is going in the future, and for this reason, DRY is often a safe choice.

Code that has been overly "dried" typically has complex control flow and too many parameters. What was initially a simple function was later extended to support a new functionality controlled by an extra parameter. After two or three iterations, the function is no longer maintainable. Fix a bug that occurs in a setting, and you introduce new bugs in other settings.

It's understandable that code quality often goes down as code evolves, but I've seen cases where a multi-parameter function with if-then-else spaghetti in the body was the result of a well-meaning but poorly conducted refactoring effort.

(1) I'm using the word "code", but this applies to design as well.

share|improve this answer
    
It'd be helpful to give an example of "too much dry" as it's the less seen end of the spectrum. –  Incognito Aug 26 '11 at 13:04
    
@Incognito: I have edited my answer. No concrete example, but hopefully what I meant is clear enough. –  Joh Sep 17 '11 at 7:48

I don't see the key points of my answer above, so here goes. Don't look at DRY so much as a rule against doing something. It may be phrased like that but it can really serve a quite different and positive purpose. It's a signal to stop, think, and find a better answer. It challenges me to look for opportunities to design a better solution. It's the good side of a bad smell in my code that induces me to rethink my design and makes me do it a whole lot better. DRY isn't about just an itty bitty syntax violation. It challenges me to modularize. It challenges me to componentize. It signals repetition that reminds me to think about using templates and code generation instead of brute force and ignorance. It helps me figure out that I should find some time to automate my automation. It leads you to a parsimonious lifestyle! It helps you to spend more of your time doing cooler new stuff rather than nitpicky old boring details. And it gives you good manners, good breath, and a healthy lifestyle! Well, perhaps I wax a bit astray ....

share|improve this answer

I have an old legacy project, where some of the former developers didn't care about DRY at all. So the whole codebase was cluttered with helper methods like GetSystemTimeAsString(), LogToFile() and lots of other stuff. Some methods were slightly customized to special needs, but most were just copy and paste.

Unfortunately some of the methods had subtle bugs like char array not long enough in some cases, using insecure stuff like strcpy(), etc.

So it was a real PITA to find all the code fragments, harmonize them and fix the bugs. And we are still harmonizing and fixing stuff.

You never know, if you made a mistake in your first method and then have to fix it multiple times, because you just copied it. And if you want to use some of the methods later, how do you know, which of the 5 methods in the codebase is the one for your case now? So you just copy one, customize it and here it starts again...

share|improve this answer

Yes, don't bother about DRY if you are writing Throwaway Code.

But DRY is important of course, if you do plan to keep the code.

share|improve this answer

<tl;dr>

I could not read all the repeated answers so I may have missed something (and will repeat it myself <= see what I did here?).

Here is the list of things that are awesome about preventing code duplication!

  1. Easier to test: you only need to test one ‘copy’ of the code.
  2. Easier to fix: you only need to find the bug in one ‘copy’ of the code and fix it once.
  3. Easier to update (same as above): needed changes, can often be handled by modifying code in very few places because you took the time to properly reuse code and did not copy the same lines to hundreds or thousands of different places in the source.
  4. Easier to reuse: when (isn’t duplicated in a few places) and is kept in generic aptly named methods, it is easy to find them and use them instead of writing your own.
  5. Easier to read: duplicated code is hard to read because it’s unnecessarily verbose; it contains a lot of lines that are not part of the logic and the specific intended functionality (for instance generic commands used to set up the stage for the action to take place or generic simple repeated tasks that are needed in many places). Clean code makes the logic and functionality pop-out because there is no repetition littering up the code-space.
  6. Easier to debug because of (1) and (5).
  7. Saves you time & money and do more fun things in the future; specifically create better more robust code. This is the bottom line and it’s a summation of pretty much all of the above. If a lot of people use the same function doFoo1(a, b), there is a better chance that many of its annoying faults & edge cases will be uncovered and resolved. If everyone copy the code and create doFoo2(specialA) ... doFuu2^n(a, b, c) then they duplicated the problems in doFoo1 and concretely created a lot more work.

</tl;dr>

Long version:

The issue with code duplication is that it "grows exponentially" (in other words, it expands rapidly) cause when you duplicate code you unknowingly grant others the permission to (for one, you’re no longer in a position to judge them) and you encourage them to do the same. You also make it harder not to because it's harder to spot and reuse useful code when there is a lot of confusing redundant repetition in the source. Especially if the code is not already extracted into an aptly named function. So if you face a common simple to solve problem you will likely write a piece of code yourself that solves it… And you will probably fail to check for a few edge cases, adding more buggy untested code.

Another thing is that to a novice this may sound like an issue that will only affect large companies but I found it afflict tiny startups just a badly (as in 10,000 lines of duplicated server-side code). It is a state of mind. You should not only master DRY but strive to encourage others to do the same; because otherwise you will doom yourself to mostly duplicate code. When the means of DRY are at hand and enforced, it is much easier to apply them. When there is lots of duplicated code, it is much easier to apply copy paste solutions.

Things I find harmful in code duplication:

  1. Is this function usable? Let’s say you find a function that does (or appears to do what you need), how do you know if it's even supposed to work correctly or if it's just code that has been duplicated and abandoned.
  2. Redundant code. Sometimes people duplicate code, use it and forget it (they can always duplicate it again in the future). At some point someone removes the calls to the duplicated function in some places in an effort to refactor but the unused function remains even if it is not being actively used.
  3. Hard to find what you are looking for. Duplicated code takes up space and makes finding useful and needed things (using tools like grep) a harder task than it has to be as you get dozens or thousands of results where you should have gotten only a handful.
  4. (Has been mentioned before): Hard to maintain but also hard to use for maintainance and regression purposes. If testing code is duplicated and not properly extracted into functions, others will duplicate it. Will anyone bother with writing an easy to use, easy to read API to make QoL better? In my experience, no, there is often something people deem more pressing matter until it gets out of hand.
  5. Code duplication is harder to read because it makes the code verbose where it doesn't have to be, in places where the verbosity doesn't add information about the intended functionality: for instance generic method calls that are used [over and over] to set the ground for multiple kinds of intended functionality, makes it harder for this actual functionality to pop out.
  6. This has been mentioned a lot. If the code is wrong, some poor gal or guy will need to search and change each use of that code. For instance if someone used a SQL injection unsafe call to mysql_query in very few places in an organized Class where it's needed, it would be easy to fix it and use PHP PDO instead but if they used it in over a thousand places copying the call over and over, fixing it will practically need to be outsourced or sometimes more dangerously, the code will need to be rewritten from scratch.
  7. Duplicating code is a bad habit. If you practice something it slowly becomes a second nature and it affects people around you. Junior devs see you do it and do it also. You should practice what you preach and make a habit of doing the right thing. You learn more. Writing non-duplicated code is harder and more challenging. It is a rewarding habit.

Last notes about overzealous code duplication prevention & summing things up:

This has also been said before but sometimes avoiding duplication causes you to "bend over backwards" and do things that are too sophisticated (or unimportant) for others to understand. Writing unreadable code (or as we jokingly call it “job preserving” code) is an issue in itself even when preventing code duplication is not involved. However, I find that if the right infrastructure and best practices are instilled from the beginning it is much easier to prevent code duplication and people often can avoid doing unintuitive things so it can prevent future unnecessary and unreadable heaps of work done for code duplication prevention if you do thing right from that start.

What is doing things right? Well that is a hard question to answer but one thing is to define which methods are needed for the project and see what has already been implemented by others (outside and) inside the company and reuse that when possible; documenting everything you do add to the codebase and attempting to make it one notch more generic than it has to be but that's it. Don't overdo design patterns just to make the code flexible where it doesn't need to be.

share|improve this answer

While I actually agree with the other posters comments about maintainability etc. all of which are valid.

I would like to add a small dissenting voice to the debate.

  • Its only important to programmers. The people who pay your wages couldn't care less as long as the software passes UAT.
  • In terms of importance it ranks well below items like getting the correct requirements, listening to the project sponsors and delivering on time.
share|improve this answer
    
as this site is for "Programmers" I think it's safe to say the question is directed at the "programmers point of view". Your statements about the wage payers, UAT, and rank of importance are valid of course, but not relevant to this specific question. –  Ozz Feb 16 '12 at 9:49
    
I totally disagree. Good management will understand the principles and why they are being done if it's explained in detail. This should be serious in-depth conversation, not a 5 minute drop by thing. –  Michael Durrant Mar 7 '12 at 3:15
    
Your second bullet point is entirely correct. –  Jim G. Mar 7 '12 at 3:19
    
@Ozz, pride in your craft is important even necessary for a good programmer, but, perhaps the "programmers point of view" ought to include a modicum of concern for "customer satisfaction". –  James Anderson Mar 8 '12 at 1:42

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.