3 ms·
I disagree with his last point. There was once a time when I was obsessive about not copying pasting code. Over time I eventually learned that sometimes it bett
by freework 12y ago
I disagree with his last point. There was once a time when I was obsessive about not copying pasting code. Over time I eventually learned that sometimes it better to just copy+paste a code snippet a few places than being dogmatic by making sure that nothing is ever duplicated. My rul of thumb is that if there is branching then don't copy, if there is no branching, then you can copy+paste it.
This snippet should only live in one place:
if (something) {
for (blah blah) {
something_else();
}
do_something();
}
on the other hand, this code can be copy+pasted as much as you like:
do_something();
do_another_thing();
another_call();
some dogmatic programmers will want to place those last three lines of code into a separate function and instead copy+paste that. But I've found that doing that sometimes makes the code more harder to read.
- justifier 12y agowhat i understood as the author's issue with copy paste was the knowledge that changes are expected if you copy: do_something(); do_another_thing(); another_call(); a few times in your codebase, but decide that you want to alter that a bit: do_something(); perform_new_feature(); do_another_thing(); another_call(); you can hurt yourself by forgetting that that alteration needed to be done to each instance just easy to maintain if you do this: function dodoan() { do_something(); do_another_thing(); another_call(); } then copy paste dodoan() as many times as you want though i do agree all rules have their exceptions and should be handled in such a way that allows for future alterations stead some obsessive mindless adherence
- sophacles 12y agoYes in that case it's a win, but it becomes an anti pattern when you sometimes want to perform_new_feature() and sometimes you don't. I've seen it happen where you end up with many functions like: def a_and_b_and_c() {...} def a_and_b_and_c_error_checked_internally() {...} def a_and_new_and_b_and_c {...} def a_and_new_and_b_and_c_error_checked_internally() {...} def a2_and_b_and_c And so on. Basically you are just putting a memorization task on calling the names rather than using the underlying bits well. Learning the balances around this is one of those "craft" bits of programming.
- sanderjd 12y agoThis is a great discussion! I've always thought that after the basic "how do I even do this at all?" concepts of programming, the next most important concept to learn how to wield and to perpetually sharpen is the "don't repeat yourself" principle. Huge swaths of software engineering literature is dedicated (explicitly or implicitly) to when and when not to apply that principle. Your parent's comment addresses by far the best argument for the principle, which is that any time something is likely to change in a snippet, copy-pasting should be undertaken rarely and thoughtfully. With the recognition that nearly any given snippet is likely to change in some way, we can conclude that nearly any copy-paste is "bad". Your comment is then a great argument against the thoughtless application of the principle, recognizing that yes, granted, nearly any snippet will change, but the required changes may well not be uniform. Personally, I think it still makes sense to pull out shared behavior for the period of time in which it is shared. During that period of time, all you have is a guess that the code might eventually diverge. If there comes a point in time when you do want the code to diverge, it is straightforward to copy-paste it back out, if branching or creating a similar method with the differences is a worse option. On the flip side, if you haven't pulled out the shared behavior, and you discover that you want to change it everywhere in the same way, it is far less straightforward to go find all those places, or even be aware that you need to do so. Another point is naming by purpose rather than behavior. To continue your example, it makes sense to ask why one method checks internally and the other externally? What different purposes do the different checking styles have? If there are good answers to questions like that, the methods can be named better, and it becomes less about memorizing name than understanding when to use which. Of course none of this is at all black and white, and I think you're spot on that this is one of those craft bits, and among the most important!
- sophacles 12y agoOne thing to keep in mind here... some of those "you can't predict" things are utterly predictable in light of experience. That part can't be faked to well, there aren't a good set of rules about it. But experience is a good teacher, and you do eventually learn to be reasonably accurate about when to do C+P vs DRY, even if you can't explain the why of it. I think it comes down to this - when I first learned to code, it was hard enough to keep track of a couple different functions. As experience came, what I could track and reason about and keep in my mental model grew, so now that I've got some experience I can see more of how the whole system will grow and interact. I'm not really smarter per se, but rather I just have more practice as putting it all together.