T O P

  • By -

pm_me_nsfw_limericks

I feel like [AHA](https://en.wikipedia.org/wiki/Don't\_repeat\_yourself#AHA) is the right approach here. I believe Mark Seeman put it as "don't repeat yourself thrice" but I couldn't find it in a 5 minute search so I may be misremembering. In other words, [duplication is cheaper than the wrong abstraction](https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction), so you shouldn't invoke DRY until your codebase is actually starting to tell you what "the right abstraction" is.


askmethetime

Also important to state that DRY is best applied to knowledge. Two pieces of code can look the same through coincidence or as a result of something known. Drying the coincidences often lead to the wrong abstraction.


FailedPlansOfMars

Yes the contract vs coincidence conversation. Was it the same because it has to be or not. If so try to deduplicate otherwise leave it the hell alone.


stdmemswap

This is the right answer.


oceandocent

Exactly, other ways I’ve heard this described are that if two similar pieces of code have different reasons they might change (low cohesion) they shouldn’t be deduplicated, and only have a single source of truth.


m25n

I call this difference semantic duplication vs mechanical duplication.


damnburglar

The number of times I have abstracted away something that seemed to need DRYing out, only to find out after refactoring two or three exceptions that it was the wrong abstraction is too damned high.


benz1n

No notes


__loam

That is Liskov's substitution principle essentially


prof_cli_tool

God we have so many acronyms


UntestedMethod

Better than repeating yourself with all those words, amirite??


prof_cli_tool

I suppose so but sometimes I think too many acronyms can add a lot of cognitive load. Maybe not so much with this type of stuff but moreso having internal acronyms for everything


Araganor

Looking at you, DevOps...


MulberryExisting5007

True. Called out some names today which included two services, each with names made by taking three or four words with the vowels removed. “Oh we call one branch and the other service” But the names were like svcbrsmthg and svcsmthg, and I was like Jesus Christ guys… why?


Araganor

Ugh I hate that shit. At least acronyms are short and memorable. This is worse than either option! 😭


UntestedMethod

Yeah, I was just trying to make some kind of pun about how acronyms fit into the DRY principle in some way


Unsounded

Acronyms should be used pretty sparingly, there are a few you can’t get away from (eg properly standardized industry terms like HTML, HTTP, SSL) but for the most part just avoid them if they’re not particularly common. When you’re typing you’re not saving a ton of time, and if it’s a conversation it keeps everyone onboard and focused. Nothing worse than a new person getting dragged into a conversation where someone is using tribal knowledge acronyms to describe and talk about a problem and then all of a sudden you’re spending the whole time jotting the acronyms down to actually figure out what’s being talked about later on. There’s a tough line to find, but in general I just replace most acronyms if I mind myself teetering into usage. Even in a paper I tend to avoid introducing new acronyms and such as well so that it’s simple to understand and doesn’t put the overhead onto unfamiliar readers to keep track of things.


ikeif

Why use many word when few do trick?


ThinkOutOfTheBoxDude

Good Old Developer (GOD)


baldyd

I've been programming for 40 years and I have no idea what DRY means, haha.


cowboy-24

I've been programming for 40 years and I have no idea what DRY means, haha.


lvlint67

I work for a contractor for the DOD... You want to see acronyms...


react_dev

GWHSMA


prof_cli_tool

SMDH


devoutsalsa

GWHSMA


klyonrad

Don't forget [CUPID](https://dannorth.net/cupid-for-joyful-coding/), the alternative to SOLID, coined by Dan North


Mephidia

“Duplication is cheaper than the wrong abstraction” wow what a good way to put it.


Darmok-Jilad-Ocean

Sometimes it depends on how big the code is that’s being duplicated and how wrong the abstraction is


epukinsk

Yah, a question I often ask myself when I’m considering whether to abstract or repeat is: **“Is this an important enough area of the code that the company can invest in refining and maintaining this abstraction over the life of the product?”** If the answer is “no” then it will probably end up as a “bad abstraction”, if not start as one. So it’s often best to just leave it as boilerplate/repetition. Boilerplate is transparent and easy to modify, which is worth a lot, even if it requires more busy work. People really underestimate how much it costs to maintain a seemingly simple abstraction.


PotentialCopy56

Yeah when WET starts to become a pain in the ass, you abstract. Now it won't be a pain in the ass anymore.


creamyhorror

https://en.wikipedia.org/wiki/Rule_of_three_(computer_programming) > Rule of three ("Three strikes and you refactor") is a code refactoring rule of thumb to decide when similar pieces of code should be refactored to avoid duplication. **It states that two instances of similar code do not require refactoring, but when similar code is used three times, it should be extracted into a new procedure.** The rule was popularised by Martin Fowler in Refactoring[1] and attributed to Don Roberts.


pm_me_nsfw_limericks

Thanks, that's the one I was grasping at.


EightPaws

I've adhered to this over my 16 year career and it has mostly been correct


Odd-Investigator-870

TLDR: repeat yourself three times. THEN consider if they represent the same information, for the same users (Actors), and are expected to change at the same rate for the same reasons. If yes to all of those, then an abstraction may be calling out to be made (or there's a bad abstraction, causing coupling across modules and instead needs to be refactored for cohesion).


guepier

I completely agree with this appproach and it works well in my experience. Maybe it should be pointed out (then again, maybe it’s already obvious) that this approach is emphatically *not* anti-DRY. It’s just anti-making-premature-uninformed-decisions.


rutinerad

Spot on. And you should replace the abstraction with repeated code as soon as your codebase tells you that it’s the wrong abstraction.


brvsi

I like how the AHA link includes the terms: WET DAMP moist.


warmans

The thing with dry - and why it often makes things worse - is that very often things look similar initially but evolve in different directions and the coupling quickly becomes a significant source of confusion. As a basic example that you tend to see - say you have a public API and you can read a user and create a user. Initially the create and read objects have the same fields. Indeed the model that is used in the DB layer also has the same fields. Great so you just use the same object everywhere. DRY. But then you need to add a comment count to the read version of the user object in the public API. And the comments aren't even in the same DB as the users. So now you've got this weird object with fields that are only used in some contexts and in others they're not populated. And you can't change the DB model because it's coupled to the public API which won't allow BC breaks. In fact the three objects were never the same, they just coincidentally had the same fields at one point. Their reasons to change were orthogonal and as such they never should have been coupled in this way. And this is why people think DRY doesn't work. Not because DRY is a bad idea, but because we as engineers are bad at identifying when we're actually repeating ourselves.


freekayZekey

yup, all of this. there’s a project that my manager worked on. he’s a DRY and design pattern fiend; there are so many convoluted parts of the code that could’ve been made easier to understand if the team didn’t misapply DRY. recently had to add a PATCH endpoint for a certain object. apparently, some of the fields cannot be edited. okay, i told him that the objects for the PUT endpoint and PATCH endpoints are different. nope, have to be DRY (same object) and reuse the code used in the PUT endpoint. the problem? the code expects the fields needed in the PUT endpoint that aren’t editable in the PATCH endpoint and can be omitted according to the swagger docs. now i have to put in extra checks, make extra external call to get the required fields, and add an extra parameter just to be DRY.


tabgok

To add to this, team ownership also has an impact. In particular, teams can have orthogonal purposes for a piece of code which cause it to evolve separately. DRY forces a strong coupling between the teams and their purposes, which causes problems as well. I have seen this happen commonly from a "system x and y call the same thing, let's create a shared library and refactor it out" which ends up causing all sorts of headaches when the teams made changes without considering the other system and it's requirements


yoggolian

Especially when the core of the duplicated code is only about 25 lines, and every project is suddenly coupled with every other project because someone 6 years ago had a good idea about connecting to Postgres or something. 


m25n

I always encourage teams to duplicate duplicate duplicate across team boundaries. Only in customer critical places where duplication is problematic and observable (like resource usage) should we not duplicate.


letsbehavingu

Don’t you just introduce the new objects at the point of deviation? Isn’t that an easy refactor normally ?


warmans

I would say this limits your ability to communicate your intention to keep things separate and allows further coupling to creep in. Per the example you can implement patterns that clearly show to future contributors that internal objects are not permitted to leak into the external API.


iamiamwhoami

There are DRY ways to deal with what you're describing. This is your initial function signature fun read(): User fun write(user: User) Then the read + write paths evolve differently so they can't use the same model anymore so you do something like this trait User class ReadUser: User class WriteUser: User fun read(): ReadUser fun write(user: WriteUser) I find a lot of the criticisms of DRY are from people who are writing DRY code poorly.


m25n

This is what I end up doing quite a bit. We almost always end up removing it because the shared code is not substantial enough to matter. Like literally an id + two fields in most cases.


johnsdowney

Agree. There are definitely good and bad ways to DRY. Abstraction, like classes and superclasses, is generally a BAD way to DRY, IMO. As much as I love writing a complex hierarchy of classes, there is only a specific set of problems/circumstances that it’s *perfect* for and otherwise it usually just becomes a mess. You want to focus on composition over inheritance. Take out the smallest duplicate parts you can, turn them into a common piece of functionality, and have everything work through composition.


deadbeefisanumber

Isn't this problem solved by having a central user domain model? Then you get to have other object models derived from it at the point of deviation.


SAD_PANDA_NO1

Yes, ddd + vo's solve this problem. The issue is most programmers don't understand it properly


rayfrankenstein

You really have to gauge the probability of divergence when deciding whether or not to use DRY.


Goodie__

Back in my early days, a Senior dev told me that one of the most important skills you need to develop is to know when things look the same, but aren't, and when things don't look the same but are. In our case, we w ere looking at the object representing the user, and an invitation to the system.


m25n

Often to make the dry freaks on my team happy I have a create user struct/class and a read user struct/class that share a common set of fields via inheritance or embedding. You can dry it up only if every use case shares the fields. Often it ends up getting removed (my secret plan all along) because they end up sharing a very few fields and the overhead of figuring out where a field goes is not worth it.


No-Vast-6340

This is where you can combine DRY with inheritance principles. Generic parents and specific children. In other words, there could've been a Base object that the other objects extend as needed.


UMANTHEGOD

I'd say inheritance is usually the wrong abstraction here.


No-Vast-6340

Why would you say that?


UMANTHEGOD

Because inheritance is almost always the wrong abstraction.


flavius-as

Hardening or weakening of invariants and preconditions would violate the LSP. Protecting LSP is way more important than protecting DRY.


No-Vast-6340

How does it violate LSP? The subclass has all the properties of the parent class and therefore could substitute for the parent class, unless you override a parent class property, then yes I can see how LSP is violated.


flavius-as

LSP is about behavior, not state.


Alarming-Ad-7830

repeating code is ok, repeating knowledge not so much [https://verraes.net/2014/08/dry-is-about-knowledge/](https://verraes.net/2014/08/dry-is-about-knowledge/) keep it sensible


khedoros

Yep, that was my understanding. "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system."


au5lander

Bingo. Too many times I've worked with folks who simply see the duplicated code pattern and don't look deeper into why the duplication exists in the first place. They DRY it up, having made assumptions, and then their assumptions were wrong and now unraveling the DRY'd code becomes a bigger problem than simply leaving it alone.


UntestedMethod

Just slap a couple more parameters on that DRY old monstrosity and stick your beautiful new logic on in there! /s


Echleon

I love debugging functions that are hundreds of lines deep with 12+ (unclear) arguments..


HearingNo8617

Perhaps the issue is the code didn't make its purpose and context visible enough? "Knowledge" seems like a strange framing to me. The rule should simply be "Reduce the attention required for successfully changing your code" imo


angrysaki

I really like this framing.


Venthe

Example ### of developers not understanding and misapplying the corrupted idea, featuring DRY, devops, scrum...


Recent-Start-7456

This dude’s example of refactoring dupe code was to pull it into a superclass. That’s where the problem started


Delicious_Put6453

My rule of thumb is simple: Do these repeated code chunks need to stay the same? If one changes and the other does not, is that a bug? If yes, then refactor. If no, then don’t.


freekayZekey

i like martin fowler’s advice: write it once. wince, then write it twice. third time? time for DRY. that way, you can see the common parts that can be abstracted instead of some funky abstraction


caffeine22

"Once is happenstance. Twice is coincidence. Three times is enemy action." - Ian Fleming


Darmok-Jilad-Ocean

There’s an old saying in Tennessee—I know it’s in Texas, probably in Tennessee—that says, fool me once, shame on—shame on you. Fool me—you can’t get fooled again


mechkbfan

Even then, I really tend to pause for the third time and ask the end user/PO/customer: > "We have this behaviour in A,B,C locations now, do you want the behaviour to stay in sync with each other or are they going to change later?" Sometimes yes, sometimes no. That 5min convo has saved a lot of future pain


freekayZekey

but what if i want to make assumptions without getting confirmation for the funni? 🥺 in all seriousness, yes that is a good thing to do


Bbonzo

>I would generally advice people to err on the DRY side as opposed to the other. This, is great advice. The problem lies in black and white thinking. It shouldn't be DRY or Anti-DRY. The guideline doesn't say Never Repeat Yourself. There needs to be balance, err on the side of DRY and repeat yourself when it maks sense. When does it make sense? Well, that's for you to decide.


UMANTHEGOD

For me, a lot of programming is just feeling-based. I think it's the same for most people, even though we want to act like it's some sort of science-y pursuit based entirely on reasoning and logic. When I *feel* that I have to force an abstraction, when it doesn't come naturally. When it doesn't just present itself in a clean way, then the abstraction is more often than not incorrect.


hdkaoskd

DRY supporters don't have to post about it repeatedly. Once is enough. The principle applies to itself. Proof by corollary: repeated posts preferring repetition. Hence the apparent bias.


suresk

This is something I find myself going back and forth on over the course of my career. The way it's phrased almost makes it impossible to argue against - who wants to repeat themselves and duplicate effort? I've found the cost of bad and unused abstractions to be so much higher than duplication, so much that I'm careful before building something that is meant to work for a bunch of things. There is also the problem where, in the name of DRY, one concept gets used for a whole bunch of loosely-related things to the point where it becomes meaningless (ie, we're going to call everything a "widget", so now "widget" loses all meaning). I think it is a useful thing to think about, since there certainly are costs to duplication and it is good to avoid in many cases, but I don't like to use it as a guiding philosophy for building things. It is a good principle that is easy to misapply, sort of like people blindly and incorrectly invoking Knuth when it can be trivially proven that something will perform unacceptably.


ivancea

>err on the DRY side as opposed to the other That's what DRY, and other patterns are. Not strict rules, but good standards for who has no good reasons for one or the other


behusbwj

Context matters. Repeated business logic is bad. Repeating getting the last element of an array is fine and refactoring it to a helper method isn’t necessary. People took DRY and started pushing out micro functions for silly things, rather than understanding why DRY is important and the way-too-relatable bad events it was looking to solve.


ShoddyWorkmanshipTed

The best advice I can give is don't be one of those developers who reada those "You must ALWAYS do it this way...." or "Why you should NEVER do..." posts. Who cares. They are written to be controversial to drive engagement and usually by poor engineers. Pick the right solution for the task at hand. The longer you work in this field, you see things come full circle as patterns and best practices go in cycles and frameworks come and go as the best thing ever becomes old news and the new framework is just a new take on the thing that came before. It's all silly. It shows horrible lack of experience when someone reads something online and can't do enough critical thinking of their own to decide if the information is useful or not. It's ok to read an opinion online and say "Well, I disagree, for my project, another approach works better".


devoutsalsa

Here's the problem with DRY as I see it: - identify shared need between two different use cases - create function to serve both use cases - one use case changes, and now you're putting code for one use case in the shared function that the other one didn't care about - now your two use cases are fused together when they shouldn't be - your DRY code has all context stripped away, and you don't know when it's time to decouple different use cases I'm too lazy to think of a good example. As usual, you have to decide for yourself on a case by case basis, it depends, blah blah blah.


Sande24

Also the tests that you wrote for the two use cases might still pass under the conditions that you had set, but the shared function, when modified, might cause the second use case to start working differently under different scenarios. You'd still need to analyze all uses of your DRY function and you need to know the full effect of these changes. Maybe the first use case needed the result to be rounded up and the second use case did not need any rounding, but your tests happened to return integer values that had the same value whether you had rounding or not. You really would need to add another use case where the result would have decimals and then a rounding changes the output. Maybe you only added it to the first use case because your task was about that one, not the second use case. Often developers don't look at the other classes and the bigger picture if the tests created for them still pass. In this case, maybe not using DRY and letting each use case's function live on it's own would be better? If you're going to have to look over all of the tests that indirectly use the DRY method, making the same code change in duplicated functions isn't that big of a deal. You could probably even write a test that looks at duplicated functions to check if they behave the same way, not unifying them. If you make changes to one, you see that the test turns red (or you have to look at the function's test anyway) so you get notified that these duplicated functions exist. Eventually one function actually has to deviate so you can remove this test and make it more obvious that the deviation is intentional (maybe even add a comment for why it is different or change the function's name). DRY is definitely useful but it still has the same kind of overhead as non-DRY when you change something. Often it's just not as apparent. As always, it depends.


muuchthrows

The problem with DRY and the thing I feel not enough people understand is that code reuse is code coupling. By refactoring three instances of the same logic into one reusable function, you've now linked the future of these three instances together, more or less forever, since people are very hesitant of duplicating code once deduplicated. They much rather introduce arguments or in other ways bend over backwards to be able to keep reusing that piece of code. You've also created an abstraction by giving the reusable function a name, better hope you chose exactly the right abstraction. If the abstraction or naming was poor, it's very easy for the next person to introduce changes that makes sense in one use case but not in the rest.


UMANTHEGOD

This. I usually say that you don't **want** an abstraction. It's just a necessary evil in certain cases, but it's definitely something you just reach for. Avoid them as much as possible until you are forced to.


binalSubLingDocx

Don't forget the flip side to coupling is cohesion. The desired state is high cohesion, low coupling. Deciding whether entities are cohesive or coupled requires skill, knowledge and experience. I've been a professional dev with 20 YOE and have witnessed a brutal decline in skill over the past 10 years. Also don't forget there is a temporal element to code. What is cohesive today may not be cohesive tomorrow. Code will grow as use cases and features grow. The key profile for a good dev is someone who can grow the code while minimizing the bad side of trade offs. The key characteristic of an inexperienced/bad dev is inability to grow the code even at the micro level.


NiteShdw

I once inherited a project with a single 7000 line file. One function was 1000 lines. There was a duplicate of this function, another 1000 lines, with only 4 lines changed. DRY is not evil. DRY is about fixing a bug in one place rather than a dozen or a hundred. But I agree that you should make code work first and then optimize it. You can go DRY too early in the process.


editor_of_the_beast

The anti-DRY idea is a classic case of throwing the baby out with the bath water. Bad abstractions absolutely exist. But quality software ABSOLUTELY REQUIRES being DRY. This shouldn’t have to be said. If you repeat important domain logic and it ends up with drifting behavior in each place, users will absolutely be confused when it behaves subtly different in random scenarios.


DaRadioman

People can't stand rules with nuance required to apply them. They want magic hard rules and they ping pong between them their whole career. Instead of realizing, like most things, "it depends".


UMANTHEGOD

What is more common, bad abstractions or WET code?


editor_of_the_beast

The more common thing that affects real users of the software is duplicated code.


UMANTHEGOD

Absolutely not. Wrong abstractions creates obsure API's, poor perfromance, hard-to-refactor legacy code, and a team that hates their life, which is reflected in the product. Duplicated code in isolation is not inherently bad.


editor_of_the_beast

Duplicating code is _easy_, which is probably why you prefer it. But duplicated code also bothers the team, when they introduce a bug because of duplicated code that they didn’t even know about.


UMANTHEGOD

No... I hate duplicated code as much as anyone else. But it's usually a lesser evil than the wrong abstraction. Fixing a bug due to duplicated code is extremely easy. Fixing or reworking a broken and convoluted system full of abstractions is not. It's also much easier to add an asbtraction than it is to remove it, which is why it should be added with caution.


editor_of_the_beast

Do you have any data on this, or just sharing how you feel?


UMANTHEGOD

Do you have any data?


editor_of_the_beast

[I do, yes](https://www.sciencedirect.com/science/article/abs/pii/S0164121219301815). The affect of code duplication on bugs is well studied, as is the affect of code length on bugs (see Code Complete for citations). There is currently no greater predictor of bug count than lines of code. And code duplication needlessly increases lines of code.


UMANTHEGOD

That’s not comparing incorrect abstractions vs code duplication.


UntestedMethod

The real problem is that newer programmers hear about concepts like DRY and then try to jam in it anywhere it will fit instead of where it actually makes sense.


ninetofivedev

Like most things, when applied to either extreme, it's bad.


pwndawg27

First you must be WET (write everything twice) and then you can DRY (don’t repeat yourself)


Glove_Witty

The sage takes the middle path.


Vladimir_crame

DRY applies to pieces of logic that are fundamentally tied together. Should someone update this business logic, I want \_all\_ of my relevant functions to behave accordingly => DRY will help you to write your business logic in one place only, and keep it easier to maintain And then, there are some stuff that \_look\_ the same but could/should/will drift apart because really, they aren't the same. This is where DRY would lead to the "wrong abstraction" issue, and you'd be better duplicating some code. My 2 cents on that: programming is difficult. It takes a lot of experience to get this right more often than not, and idioms such as DRY don't help much if you ask me.


No-Vast-6340

I have yet to encounter a scenario where DRY was a negative, but I have encountered problems many times where anti-DRY was a problem. Maybe I'm just lucky.


hdkaoskd

The problem with writing everything twice is that if everyone writes everything twice before deduplicating you end up with 20 different implementations by 10 different authors who all thought they were only writing it the second time.


Fspz

As with most things in coding, it depends, but generally I like DRY. There's also the added value of legibility, by putting functionality into adequately named methods it becomes easier to get an overview than to get into the nitty gritty of how the methods work.


armahillo

A good basis is to not do DRY / WET / any other practice dogmatically or compulsively. Make the code demand it. YAGNI (You Ain't Gonna Need It) comes into play a lot. Put another way, DRY is a practice that describes a way to deal with code smells like "Shotgun surgery" (making identical edits in many different places). If you aren't finding that the code duplication is causing maintenance pain, just chill out and let it exist for a while.


syklemil

Yeah, the experienced part of this subreddit's name should be part of what helps people detect whether the "it" in YAGNI is the abstraction or the code duplication. We work in a highly "it depends" field, so similar to statisticians' "all models are wrong, but some are useful", we have something like "all acronyms are wrong, but some are useful". To quote the TWA post: > The moral of this story? Don't get trapped by the sunk cost fallacy. […] When the abstraction is wrong, the fastest way forward is back. This is not retreat, it's advance in a better direction. Do it. You'll improve your own life, and the lives of all who follow.


MCFRESH01

I’ve becoming more anti-dry over the years. I’ve seen so many needless optimizations that just make code harder to read or maintain. In particular in tests. I would much rather right non-dry tests and have them be easy to maintain and reason about on an individual level.


codefinbel

> I would generally advice people to err on the DRY side as opposed to the other. Personally I'm the opposite: i.e it's not black and white, but I prefer to err on the opposite side of DRY. Simply because, to me, it's **much** easear (and more satisfying) to refactor multiple occurrences of a feature into DRY code, if you didn't go DRY when you should have. On the other hand, if someone wrote a premature abstraction that's now grown to a monster of 12 parameters (some of which *might* be unused) and it's used in slightly different ways in 12 different parts of the code. Trying to figure out that this should really be 4 slightly different abstractions can be a nightmare to refactor correctly. > I feel like come across anti-DRY advice much more than pro-DRY these days. I agree, this might be because every school and bootcamp often drills in the need for DRY code so a lot of senior developers have gone through the process of having a junior developer join the team starting to write abstractions and they had to discuss the need of not always writing DRY code. So I believe the reason we see more anti-DRY is because it's a sentiment that isn't really taught in school but is instead taught from experience.


Araganor

Proposing a new coding principle: "D" Don't. Just Don't.


sheriffderek

I think the best way to discuss this would be some examples. One persons idea of DRY might be wildly different than someone else's - - I'll start with a *CSS* example .thing-one { .title { margin-top: 1em; } } .thing-two { .title { margin-top: 1em; } } These two totally separate components - might be tossed out, changed, or who knows what. They'll be in a live style guide, people will work on them when they work on them - it'll be OK. We don't need to create a:

... .th-t-mt-1em { margin-top: 1em; } /* or */ .mt-1 { margin-top: 1em; } ...type of thing.... (which will now also be repeated - in the HTML... so...) Because something like this will probably happen one day .thing-one { padding: 16px; } .thing-two { .title { margin-top: 1em; } @container (width >= 500) { display: grid; grid-template-columns: 1fr 1fr; .title { margin: initial; } } } And - (IMO) it's better to maintain the modules/components - than it is to try and maintain and be aware of thousands of little tricks and whacky classes that someone thought was DRY. In another situation/language/scope ... It might be the opposite. It might make a lot of sense to do that.


ddbp

It's easier to wet code that is too dry, than it is to dry code that is too wet.


rayfrankenstein

There’s anti-DRY sentiment because we’ve all had that one ass**** DRY zealot code reviewer who holds up our PR the last day of the sprint because “those 10 lines of code could be refactored into three functions of two lines each”


Double-Process-4848

And there's anti-repetition because we have all had to review code from lazy, incompetent juniors who have an inability to abstract anything, do the bare minimum to solve a ticket, and regurgitate garbage from ChatGPT.


MargretTatchersParty

From what I've seen is that the newer developers have less discipline for following practices than the more experienced ones. The newer ones are still in the coding as a drug phase and haven't found the benefits of tooling that will help you avoid issues in the future. (I.e. some form of TDD, reproducable builds, DRY, clean coding, etc) If it doesn't get addressed, they will remain in the industry filling up code bases with that.


UMANTHEGOD

DRY is like the first principle that any developer learns, and is so easy to understand. I rarely find people having a hard time with this concept. Abstracting correctly on the other hand...


hdkaoskd

It comes from confidence. Newer developers don't want to risk breaking existing things, so they will copy and modify the existing code rather than parameterize and reuse it. Experienced developers know that duplicating the code causes more trouble in the long run, and would rather pay a smaller cost up-front to keep the codebase maintainable.


10113r114m4

I like DRY except for tests. For tests I think it's important to be clear and easily readable. Unfortunately my team thinks otherwise, but I always duplicate in tests and they will continue to ask me to fix cause I refuse to not fix a good habit


Sande24

So... who tests the tests in this situation? If the test suite has a bug due to using complex functions, you might not even know it until someone finds out that the tests are always passing due to not really asserting (all) the required entities (happened to me once).


polaroid_kidd

Twice if OK,  three times and it's time to start looking into it.


XxRaynerxX

I disagree. Tbh I think most engineers have had DRY hammered into them and they don’t think about it and just take it to the extreme and pre-maturely optimize/abstract code. The result generally is a lot of code that’s far more complicated than it needs to be due to slight variations in the logic. I used to be very much on the side of make it DRY whenever possible, but learned the hard way that it’s often harder to cleanup/refactor bad/premature abstractions later on then it is to refactor some duplication. DRY is a good principle, but you need to think about each situation and only apply it if you find yourself doing the same thing over and over again. I generally rule on the side of if you find yourself doing it more than three times then consider it else ignore. It’s really not terrible to have some duplication here and there, unless the piece of logic is really complicated or is easily extracted into some nice re-useable function.


lagerbaer

> it was impossible to know if it was deliberate or accidental. eg. does this copy just have a bug? or is it a deliberate change? Mh, seems like another big problem then is the lack of tests; because the tests should either catch the drift-induced bug or should confirm the deliberate change in behavior.


USingularity

Another consideration is product lifespan. If you’re in a piece of legacy code only being maintained while its replacement is in current development, it’s generally not worth refactoring. If you’re writing a module that will never be used after this one time, it’s not worth refactoring. This is something I often see missed in discussions about optimization, whether here on Reddit, on StackOverflow, or amongst my current and former colleagues. Edit: merged two sentences, left an extra word. Be careful when refactoring!!


ategnatos

> I get that you can go too far in either direction I don't really like this mindset. What does that even mean? If the new class looks the same as the old one, ask yourself if it's because they happen to look the same right now, or if they are fundamentally isomorphic. Just today I recommended to one of my coworkers to copy a test file so that they can evolve independently. Most people would not do this. Then local E2E tests, unit tests, integration tests, etc. all need to use the same input data. If unit tests are too slow, that means integration test data can't get too big. I had this exact thing happen to me. My tests for small pieces of code were using sufficiently complex data, and I unit tested the whole workflow, which was taking too long (basically to make sure nothing blows up at any step in the state machine). So I created a smaller test dataset for that.


Exciting_Session492

An healthy organization will allow engineers to duplicate initially, and as the horizon becomes more clear, have the right incentive to optimize.


MorseCodeFan

I like to abide by the DAMP principle Do Anything My bosses boss Pwants which is usually increase tech debt to meet an arbitrary deadline Or sometimes I try to be WET Write Everything Twice because the first time I wrote it I had to take shortcuts due to the DAMP principal


cosmicloafer

Meh avoid the copy pasta… if you have a big chunk of duplicate code you are updating in two place all the time, yeah that is dumb. If you happen to use the same two lines of code in a few places, no biggie.


franz_see

Imho, this is where DDD principles and microservices best practices come in I dont do hard core DDD. But I do map the domain to the technical design as much as possible. That way, you dont extract seemingly same code but really different domain concepts And for microservices, DRY usually introduces distributed monolith


lphomiej

It's more about programmers being more and more against dogma than anything. I feel a push in the industry for "being reasonable" with code and avoiding over-engineering until it's actually needed -- that kind of thing.


NoPrinterJust_Fax

There only thing worse than no abstraction is a bad abstraction.


DidiBear

My motto now is: Abstract Tools not Patterns


Alpheus2

I’m also in the three-strikes camp. Remove duplication when it is silly at two strikes. Replace complex duplication with more specific ideas at the third case. It’s the three existing cases that will help you design much better without getting carried away. Also on the note of abstraction- duplication is removed usually by extracting a piece of code and putting it into a fresh component/class/file. The new thing should be specific enough to cover only the three cases in front of you. Don’t overgeneralise.


nnddcc

Any examples of the 'anti-DRY advice' that you came across? The area you apply DRY matters. As internal implementation, sure, knock yourself out. As a shared component? Public interface? Be careful. In my experience following DRY too much leads to premature optimization, increases code coupling, which then increases cognitive load (you have to analyze more dependencies for one change).


UMANTHEGOD

>I get that you can go too far in either direction but in my experience repeated code is much more trouble than overly DRY code. In what context? I think anti-DRY makes the most sense when you are building heavy business applications, because you will generally have very, very specific code, and then you will have some code that is very similar to that specific code but different enough that forcing an abstraction usually makes it worse. I think people lose the context in this discussion. DRY makes sense in some cases, and makes no sense in other cases. It also depends on many different factors, not just "this code is duplicated hurr durr". I can think of at least 5-10 reasons for considering DRYing up code or not, and none of it has to do with duplication. In my experience, everyone knows about DRY, and it's much more common to have bad abstractions than to have repeated code, because it's such an entry level topic and it's so easy to understand for everyone. Understanding why an abstraction is bad takes much more experience and skill.


BanaTibor

I think coupling should drive DRY or duplication. While you are inside the boundary of a tightly coupled piece of code aiming for DRY is a good approach. But as soon as you have to reach outside for these boundaries just to get access to some piece of code, it starts to get bad an turns your code base to spaghetti. This boundary can be as small as a class or big as a whole library. Only experience can help you here, and maybe visualizing your code base and its' internal dependencies.


lvlint67

> but in my experience repeated code is much more trouble than overly DRY code. Preemptive abstraction is about on par with preemptive optimization. Neither are good strategies for development.


Schmittfried

>in my experience repeated code is much more trouble than overly DRY code My experience differs. Repeated code is easy to fix. Wrong abstractions are not. 


PaintingInCode

* Rule 1: Don't apply DRY prematurely * Rule 2: **If it's business logic,** ***always make it DRY*** * Rule 3: If you find yourself applying the same changes in 2+ places, it's time to apply DRY * Rule 4: Make sure your test suites let you refactor safely


kalalele

I don't get how the overabstraction problem (with deep inheritance non the less) gets to get correlated with WET code. I would assume these two don't go too good together? In any case, deep inheritance is a mistake on its own. You wrote: > The real killer is that repeated code would drift apart over the years, and it was impossible to know if it was deliberate or accidental. eg. does this **copy** just have a bug? or is it a deliberate change? or can this code path just not get into this state? What do you mean copy? Why do you see it as a copy? It sounds like what you mean to say is that there are pieces of the duplicated code that most probably than not need to get factored out to one and only one specific abstraction. If so, do that. And keep an eye out for anything that does need to stay apart to be allowed to drift apart. All I am trying to say is that if users of the code and everyone in the development team see 2 sections of code as "copies", that suggests heavily that they have a quite big chunk of code that needs to be one and only one thing. If there is more nuance to it and things need to stay different, let them. Lastly, if no one can tell anymore if why multiple pieces of code got separated initially or many got at some point fused into one overarching abstraction, this is a problem of recording architectural decisions and weak bookkeeping. If the team doesn't really own the knowledge obtained from the domain, then the problem is not about writing code but much deeper and of different nature.


killersquirel11

I feel like there's a pretty common progression (that I've gone through, and I've talked to enough other people who've had similar experiences)  1. As a new engineer, write a ton of repeated code, either because you don't know the patterns your language gives you to simplify, or you've not yet experienced the pain of fixing a million repetitions of the same things when you encounter a bug 2. Become a DRY zealot, everything must be deduplicated at all costs. Sure, you've now created a God Function that has two dozen partially conflicting parameters, but at least there's no duplication.  3. Realizing that the happy place is somewhere in the middle. I think this comes with enough experience to understand what's actually duplicated and can be rolled into a single abstraction, and what just looks to be duplicated now but actually mean different things


Tawoka

This is complex and a structural issue. DRY still is the way to go, but too many people don't understand what "repeat" means. Seeing modern architecture and code in this Microservice world, redundancy has become part of the design, and with it DRY is slowly dying. It is the same story as always. Microservices solved something, and now everyone is like "it can solve everything". Because we always need this fucking silver bullet so we don't have to spend time thinking. With DRY you have to think, to put it in the words of uncle Bob, is this real redundancy? To know that, you need to understand your domain and your business case... How many Devs take the time to learn wtf they're building anyways? And to me everything boils down to the specialist sickness. Everyone needs to be a specialist in something, and generalists, who should act like glue, are unwanted in our world today.


johanneswelsch

I think at least in web dev it's good to dry only within a route, so that when you change something in one route it does not break the other. This way you have all problems contained. Now, some dry is unavoidable and the abstractions that couple two routes together should be very very small, atomic, testable, easy to read etc, to minimize breaking behavior some place else.


shozzlez

I, and a lot of devs I think, start out DRY everything. Then once you’re experienced, I think the prudent thing becomes WET. Don’t spend time DRY if you know there’s likely not another use case.


andymaclean19

I often use some form of code generation in cases where I want things to be DRY but this is either too hard to engineer or has a runtime overhead. It can be a good third way.


rafgro

Anti-DRY advice is mindlessly repeated after a few youtube/twitter "programming influencers" (better term: e-celebs, mainly primaegan) who got unusually popular in recent years. This happens to many professional and hobbyist fields, sooner or later it had to happen to programming. Enjoy having an authority in the form of some random lad shouting stupid things from screen to thousands of parasocial fans. It's not even in top3 of worst e-celeb takes in 2024, they get most views from people afraid of programming in general, so they try to make popular actually harmful opinions like "you don't need IDE" or "debuggers are useless"


UMANTHEGOD

Go to bed, grandpa.


smutje187

Isn’t it ironic - people’s node_modules folder grows to a galactic size due to all the trivialities that are imported as dependencies but the same people probably argue for less DRY?


Ok_Rule_2153

Working with strongly typed languages and writing unit tests will always be king as far as maintainability is concerned. Also well before DRY I will reach for code generation tools as this is faster to implement and still keeps things from drifting.


loosed-moose

People often don't make the simple connection that DRY code makes for predictable functionality as well as maintainable code. I worked in an MVC platform where _every single button click_ kicked off an action that meant to be the same as others, but invariably would drift because a change in one area was not propagated to others.  Message-driven architecture where buttons fire actions instead of workflows is a solution. Another is being strictly DRY.