4 ms·
> Consider duplicate code to be a mistake I partially disagree. Some of the worst code I’ve seen was an attempt to reduce duplication. It’s easy, especially i
by christophilus 8y ago
> Consider duplicate code to be a mistake
I partially disagree. Some of the worst code I’ve seen was an attempt to reduce duplication.
It’s easy, especially in UI code, to mistakenly identify duplicate code, and prematurely build a DRY “solution”. Premature DRY is the root of much evil.
I try to follow the rule of three before trying to identify duplication. Even then, I’ve become much more cautious than in my younger days.
- preommr 8y agoIt's hard because you often don't know what you don't need before you don't need it. Duplication for < n is good in principle until you realize some coworker decided to make their own system or went way past n with end result being it'll take more effort to refactor than to have initially designed something robust.
- gmueckl 8y agoIt really depends on how complex the thing is that you have n copies of in your codebase. Also, is it a concrete algorithm? Is it a pattern? A concrete algorithm can usually be factored out more easily than a pattern, because the later needs to be flexible and extensible. Supporting all the variations of a pattern in a complex codebase in a single place can be challenging and in the end it may be harder to get right than maintaining the repetitive code.
- miclill 8y agoBy "rule of three" you mean if code exists three or more times you deduplicate? Because wikipedia says this about rule of three: [https://en.wikipedia.org/wiki/Rule_of_three_(writing) https://en.wikipedia.org/wiki/Rule_of_three_(writing)]
- TravelAndFood 8y agoYes, like if you see the same few lines of code in two different places, you leave it, but in three different places, you make a separate function.
- Rexxar 8y agoYou need at least document the duplications when you see it (especially in big code base). Because the rules of three easily become the rule of 87, if you are not aware of the 86 other "second version" that have been written by people that were knowing only one of the duplicated versions.
- harimau777 8y agoIn my experience, instances where removing duplication made code worse have usually been because the code is similar in its mechanical behavior but very different in its purpose. I wonder if a rule of thumb could be to judge duplication more on the purpose or function of the code rather than the mechanics of how it is implemented. For example, suppose you have two promise based functions with error checking; one of which is doing a fetch and the other is performing a complex calculation. The error checking blocks might be the same since they are mechanically performing the same tasks; however, since the errors they are likely to see are so different a generic version could make the code more difficult to understand.
- Aardappel 8y agoThis "rule of three" seems to be fashionable these days, but I think you'd want "the rule of 2" all the way to "the rule of 10" depending on the size of the duplicated code, how hard it is to abstract, the complexity of the resulting abstraction, the distance between uses and abstraction, etc. I certainly wouldn't want to wait to 3 if I am copying a block of 10+ lines that requires just a single int parameter to abstract into a function very local to the 2 uses. And I may use "the rule of never" on a 2 liner that requires a callback to abstract.
- brianpan 8y agoIt's a rule of thumb. You don't refactor if and only if three duplicates exist.
- fiddlerwoaroof 8y agoIn my experience the worst messes I’ve had to deal with are cases where people didn’t DRY enough: it’s easy to “unDRY” a function: you just duplicate it and rename it when you notice that its code serves two different purposes. It’s harder to take duplicate code and deduplicate it, because you have to think really hard about the context and whether or not you’ve encapsulated the environment correctly.
- Aardappel 8y agoThat really depends. A single function is often easy to unDRY, but some abstractions, in particular when stateful objects, hierarchies or layering come into play, are incredibly hard to unDRY. Even just spotting that they can be unDRIED without side effects can be super hard, making it such that most people won't have the courage to do it. Now you are much more stuck with this code than any repeating code. I'd claim that _on average_ DRY-ing is easier than unDRY-ing. This assumes the repeating code is intentionally kept simple and linear. The absolute worst code is a mix of repeating code and half-assed abstractions, where the "perfect refactoring" would have to "move" or "redo" abstractions. This to me is even harder than DRY or unDRY.
- gmueckl 8y agoYes. I think one of my most important learning experiences was to not be too strict about code duplication. There is a cost for duplication, but there is also one for DRY. The first one is just more obvious. What it really comes down to is to recognize the true costs of each option. For me personally, this was one of the biggest lessons I learned that made me see large codebases and working in a team in a new light.
- beetwenty 8y agoOne trick I use is to identify whether the duplication involves something that would ultimately be the domain of different modules. If it creates a dependency across modules, it's a design problem significant enough to probably not result in the abstraction I have in mind right now, so I should let it rest and see how they evolve. And there are definitely cases of duplication too small to matter - which as you note is often the case for UI and UI-like things. You often end up in a position where the most reusable code is, in fact, the copy-pasted code, because the only thing it does is describe a permutation of abstractions and assets glued together. Sometimes this code is not the optimal code, but that's a problem that can be returned to down the line. The easy picks for DRY are one-liner functions that describe a preconfigured intent, like "draw a centered bounding box". That's something your glue code will crave, and there's little issue with deduplicating it later. But even there some tension arises since you can always decouple a little further by having those functions rely on an imperative context where most of the state(e.g. the size or color of the box) was previously configured, versus specifying it at the callsite. After certain thresholds, there's a flipflop between wanting to code it and wanting to configure it.