this post was submitted on 25 Aug 2023
41 points (100.0% liked)

Programming

423 readers
2 users here now

Welcome to the main community in programming.dev! Feel free to post anything relating to programming here!

Cross posting is strongly encouraged in the instance. If you feel your post or another person's post makes sense in another community cross post into it.

Hope you enjoy the instance!

Rules

Rules

  • Follow the programming.dev instance rules
  • Keep content related to programming in some way
  • If you're posting long videos try to add in some form of tldr for those who don't want to watch videos

Wormhole

Follow the wormhole through a path of communities !webdev@programming.dev



founded 1 year ago
MODERATORS
 

The "don't repeat yourself" principle is well established, but over-aggressive refactorizarions to extract common code are also widely known for creating hard to maintain code due to the introduction of tight coupling between components that should not be coupled. A passing resemblance between code blocks is reason enough to extract them away, even if that ends up breaking Liskov's substitution principle.

To mitigate problems caused by DRY fundamentalisms, the "write everything twice" (WET) principle was coined. WET works by postponing aggressive refactorizarions, the kind that introduces complexity and couples unrelated code just because it bears some resemblance, by creating a rule of thumb where similar code blocks showing up twice in the code should not be refactored, and only code that shows up multiple times should be considered for this task. However, this rule ignores context and nuances, and can dissuade developers from cleaning up code.

So, where do you stand on the topic? How do you deal with duplicate code? Do you follow any specific rule of thumb?

top 18 comments
sorted by: hot top controversial new old
[–] Phoenix@programming.dev 12 points 1 year ago

If I find myself repeating more than twice, I just ask "Can this be a function". If yes, I move it there. If not, I just leave it as it is.

Life's too short to spend all day rewriting your code.

[–] atheken@programming.dev 11 points 1 year ago

Important/generalized patterns reveal themselves over time. I generally push for people to just write the specific thing they need for the given context and then if the same pattern shows up elsewhere, promote the code to a shared library. It’s really hard to anticipate the correct abstraction right from the start, and it’s easy to include a bunch of “just in case” parameterization that bloats the interface and adds a lot of conceptual overhead.

That being said, with low-leverage/complexity code like html views, repetition isn’t as problematic. Although, I do think that HTML components have matured enough to be the correct unit of abstraction, not CSS classes.

[–] Kissaki@feddit.de 6 points 1 year ago* (last edited 1 year ago) (1 children)

What does the code represent? What does it concern?

Focusing on the code and pattern too much may mislead. My thinking is primarily on composition and concern. The rest follows intuitively - fee with risk, gain, and effort assessment.

I've had occasional instances where code duplication is fine or not worth to fix/factor. But I feel like most of the time distinct concerns are easy and often important to factor.

[–] PoisonedPrisonPanda@discuss.tchncs.de 4 points 1 year ago (1 children)

I've habe

found the german.

[–] Kissaki@feddit.de 2 points 1 year ago

Lol, I guess I habe wrote it on mobile with autocorrect 🙃

[–] fiat_lux@kbin.social 6 points 1 year ago* (last edited 1 year ago)
  • When writing new code: make a good faith attempt to DRY (obviously not fucking with Liskov in the process). First draft is very WET - deleting things is easiet at this stage anyway and accidentally prematurely DRYing causes headaches. Repeat the mantra "don't let perfect be the enemy of good" to prevent impulsive DRYing.
  • When maintaining existing code I wrote: Refactor to DRY things up where it's clear I've made a conceptual misjudgement or oversight. Priority goes to whatever irritates me most in the moment rather than what would be most efficient.
  • When altering other people's code: Assume they had a reason to WET (if you're lucky, read the docs that discuss the decision) but be a little suspicious and consider DRYing things up if it seems like a misjudgement or oversight. Likely realise 50% of the way through that this is going to take much longer and be way more painful than you hoped because of some esoteric bullshit compatibility issue. Curse yourself for not letting sleeping dogs lie but still start engaging in sunk cost fallacy.
  • When reviewing PRs: Attempt to politely influence the writer to DRY it up but don't take too much offence to WET if it has a decentish reason in context. Throw in an inline one-liner to not make other maintainers question their sanity or competence when they realise they're reading duplicate code. Also to more reliably establish git blame rather than blaming the next poor fool who comes along and make a minor reformatting revision across the file. Include a date so that someone can stumble across it in 10 years as an archaeological curiosity and their own personal esoteric bullshit compatibility issue.
  • Long term maintenance: Live with your irritatingly damp mouldy code. There's new code that needs to be written!
[–] sip@programming.dev 6 points 1 year ago

I prefer the FP approach where I create smaller functions that I compose together in larger functions or methods wich rarely repeat themselves elsewhere identically. Forcing extractions and merging of such functions often leads to weird code acrobatics.

[–] oessessnex@programming.dev 4 points 1 year ago

The implementations mostly don't matter. The only thing that you need to get right are the interfaces.

[–] Kache@lemm.ee 4 points 1 year ago* (last edited 1 year ago)

WET/DRY-ness is like a property of code -- a metric or smell perhaps, but not something to goal towards. That's like asking whether you drive fast or slow and whether we should all drive faster or slower.

[–] tatterdemalion@programming.dev 3 points 1 year ago* (last edited 1 year ago)

I don't think DRY or WET or the "rule of 3" can address the nuances of some abstractions. I think most of the times when I decide to abstract something, it's not because the code looks repetitive, but because I've decomposed the architecture into components with specific responsibilities that work well together.

In other words, I don't abstract from the bottom up, looking at repetitive code. I abstract from the top down, considering what capabilities are required from a system and decomposing the system to deliver those capabilities.

Of course, repetitive code might be a smell, suggesting that there is room for abstraction, but I don't design the abstraction based (entirely) on the existing code.

[–] Ogeon@programming.dev 3 points 1 year ago

I liked this talk on the subject: https://www.deconstructconf.com/2019/dan-abramov-the-wet-codebase

It's a nice explanation of how it's less about code that looks the same or currently performs the same operations, and more about what it means.

[–] MonkderZweite@feddit.ch 2 points 1 year ago

Of course functions still shouldn't do more than one or two things. Hence they don't get too complex.

And yeah, duplicates to avoid thight coupling (if still needed) are fine, if kept in moderation.

[–] hypertext@feddit.de 2 points 1 year ago

I can very much recommend taking a look at the AHA principle. Kent C dodds has a short and good presentation on it. That's what i go by these days

[–] ursakhiin 2 points 1 year ago* (last edited 1 year ago)

If the only difference between two classes or structs is hard coded config, rewrite to be a single implementation and pass the configs in.

If it's more in depth than that it may not be worth refactoring but future copies should be designed more generically.

[–] Alfiegerner@lemm.ee 1 points 1 year ago

It depends on how much regression testing is in place to test that old and refactored code behaviours have the same outputs, and how much budget there is for writing this tests.

For old financial systems for example, the answer is often to repeat the code again, as there's likely to be little tests to confirm existing behaviour and writing tests around very complex business domains is prohibitively expensive.

[–] robinm@programming.dev 1 points 1 year ago

There are 2 metrics that need to be considered:

  • easy to read
  • easy to modify

The first point is by far the most important. Usually DRY win because less code means less to read so less to put in your head. But if the abstraction is too complicated (for example because there are two many parameteres) then it's worth considering drying.

And don't forget the second point, but do not overthing and YAGNI. Sometime a simple comment “don't forget to update method foobar()” is enough. Don't forget either that you can always rewrite the abstraction when you need to modify something (if the original did not fit your new requirements), but for this to be an easy task, the understanding of the original abstraction must be crystal clear. That's why the first point is so important.

[–] nothacking@discuss.tchncs.de 1 points 1 year ago* (last edited 1 year ago)

I just move any duplicate code into a function, no issues yet. (In face, fixing a single bug often ends up fixing multiple problems)

[–] huntrss@feddit.de 1 points 1 year ago* (last edited 1 year ago)

I tend to go with WET and I read one or two articles that introduced WET and explained one of the missunderstandings of DRY: It is about sharing knowledge and less about sharing code. Therefore as me tioned by another poster: it makes sense for business logic but less so fir everything else.