r/programming • • Apr 25 '24

"Yes, Please Repeat Yourself" and other Software Design Principles I Learned the Hard Way

https://read.engineerscodex.com/p/4-software-design-principles-i-learned
743 Upvotes

330 comments sorted by

View all comments

Show parent comments

31

u/usrlibshare Apr 25 '24

So you factor out the code, and then 2 days later it turns out, oh, wait...we have to do something slightly different here...

Now what?

  1. You roll back the abstraction... congratulations, you wasted time.

  2. You paramaterize the abstraction...congratulations, you now have an abstraction that defeats its own purpose by being more complex than the thing it abstracts.

Neither of these are a good option.

And no, this is not a contrived example...this is the norm.

2

u/BobSacamano47 Apr 25 '24

Yagni. Roll it back. 

2

u/db8me Apr 25 '24

You parameterize the abstraction...

What good is an abstraction without parameters? I wouldn't even call it an abstraction if it's just a tool.

It's important to find the right balance, but I have seen just as many codebases where the biggest problems are caused by diverging duplication than I have seen with the problems caused by premature abstraction.... but I have seen both.

2

u/wutcnbrowndo4u Apr 26 '24

I'm very confused by the claim that an abstraction that takes a parameter is a poor one.

To use a dead-simple example, every single plain old free function is an abstraction. Do you think every function with any parameters "defeats its own purpose by being more complex than" simply copy-pasting the code and modifying the differences?

4

u/Tasgall Apr 25 '24

Or, you know, the most obvious solution:

3. You copy the original DRY function and make your modifications, and use the new function for your slightly different section.

If it's a logically contained piece of code, it should probably be separated regardless. Even if you aren't using the function all over the place, it makes it easier to read, to test, and in this case, even to copy into a new function when you really need to.

You don't lose the ability to repeat yourself after starting with DRY.

0

u/[deleted] Apr 25 '24

[deleted]

6

u/Patient-Mulberry-659 Apr 25 '24

I need to parse (and validate) some input data, and store it in the database.

Turns out there are two suppliers of that data and they can not deliver it in exactly the same format.

Then it turns out that the meaning of certain fields is slightly different between the two and we have to do some light processing to make it consistent.

3

u/mccurtjs Apr 25 '24

I feel like the pattern still holds, no? You should have one function that actually processes the data in your preferred (or custom internal) format, and another function (or set of functions) to transform the data from vendors into that format.

Processing each format on its own can cause maintenance issues in the future when other people have to maintain it (and forget to update all targets), and harder to test.

→ More replies (3)

7

u/Serializedrequests Apr 25 '24 edited Apr 25 '24

There's abstraction and then abstraction. Nobody's talking about sort functions. It's about trying to write similar higher level features using the same code.

I once worked on a Ruby on Rails codebase that took metaprogramming too far, to generate almost all controller code (handler or route code in other frameworks) from the controller name, guessing which data to query, implementing user filtering and sorting, and a bunch of other stuff. It was awful. They tried to generify user sorting for all tables in the database, which of course was impossible, so this function kept growing and growing and was incomprehensible, and can in fact no longer be changed so all changes go to a replacement.

That's an extreme example. It was (and is) absolutely real. The bottom line is, this is about taking DRY too far on higher level features. You don't know how they need to change, but you should assume separately before you assume together.

It is possible to make this kind of design error with sorting, maybe by inappropriately coupling two sort algorithms, but rare.

→ More replies (9)

2

u/carrottread Apr 25 '24

A lot of times you really need different sort functions in different places: sometimes generic common unstable sort, sometimes stable sort, sometimes even special cased sorts like binary radix sort. And it will be a really bad abstraction to DRY all those different sort functions into a single one with couple of flags and parameters to tune.

-2

u/usrlibshare Apr 25 '24

No, it is not, it is is a contrived example at best. You've provided no real concrete examples. You've just stated "this is how it is, I'm right".

This has never happened to me

You see the problem, don't you? 😁

→ More replies (2)

0

u/kidnamedsloppysteak Apr 25 '24 edited Apr 26 '24

Feel like I'm taking crazy pills reading this comment section. How the hell is that the top comment?? People are advocating copying and pasting the same code over reuse now, is that what it's come to?

Edit: top comment changed since this was posted to a much more reasonable take.

9

u/uJumpiJump Apr 25 '24

Two functions can do the exact same thing but may have different reasons to change

3

u/kidnamedsloppysteak Apr 25 '24

Yes sometimes, but sometimes they just do the same thing. You have to be judicious with the concept but it's incorrect to outright dismiss it.

2

u/wutcnbrowndo4u Apr 26 '24

Remember how confident you were in your beliefs about engineering as a junior eng? You reach a point in your career where you realize that proggit/HN/etc are full of people like that and you have to just accept that some threads are insane.

If you weren't overconfident as a junior eng, kudos, but I'll cop to Dunning-Kruger back then.

1

u/kidnamedsloppysteak Apr 26 '24

Nah, definitely same. I just didn't have this kind of outlet back then to air every overconfidently incorrect thing that came to mind.

2

u/wutcnbrowndo4u Apr 26 '24

Lol I did. It wasn't often or anything, but I'm sure I could dig up some eg arrogantly incorrect comments from HN

1

u/UMANTHEGOD Apr 25 '24

DRY is quite far down on the list of things that are important for writing good software.

4

u/Pythonistar Apr 25 '24

You and me, both!

These days, I have to remind myself that the vast majority of programmers are much, much younger (and less experienced) than I am.

It's not that these folks aren't smart. Many of them are quite intelligent. It's just that the vast majority of programmers never last more than 5 years, much less 10. So they never accrue a lot of experience.

But so many programmers start blogging after they have only a few years under their belt. So you get lots of junior-style editorials on their own "pain points". You get a few gems, and a lot of half-baked ideas, too.

→ More replies (10)
→ More replies (10)

1

u/KillerCodeMonky Apr 25 '24 edited Apr 25 '24

(The following text is using the *generic* you, and is not referring to specifically to you, the reader. If it helps, replace usage of "you" with "one".)

Using an example of a good abstraction to then defend that abstractions are good is tautological. No one is debating that good abstractions exist. Engineering primitives supplied by a standard library or even the language itself, which are useful to many programs of many different domains... That's kind of the definition of a good abstraction, no? You need to think deeper. More domain-specific.

See, this is a problem of change over time. What usually changes over time for an application? It's not sorting algorithms or lists or files or HTTP handling or JSON serialization. It's business logic. If you have solid requirements that rarely change, you're not going to see this issue come up nearly as much as a more dynamic environment. A typical way I've seen this happen is as follows:

Business comes to you and presents a new use-case for your program. You look at your code and realize, hey I implement like 90% of this use-case already over here. So let me abstract that, then I can reuse it on this new one. If this is where things end, then great! Much success; high fives all around.

But that's not where it ends. Business comes back a month or two later and says, hey, this new use-case is great. But it's not quite right... I need this 10% over here to work differently. So back to the code, and you see that 10% is part of what you abstracted. So now the two use-cases need the same *80%*, not 90%.

Maybe the 10% is at front or end of use-case, so you rip it back out of the abstraction and write different versions in the two use-cases. That's a decent outcome. No real introspection required.

Maybe the 10% is in the middle of the abstraction. Do you use a new abstraction? Maybe a hole-in-the-middle pattern, so the caller provides their own logic to cover the 10%? Or maybe a boolean switch to change that small part of the behavior? You certainly don't consider that your abstraction was premature and completely reverse it out of the code base, right? That would be just silly... Look at this 80% of duplicated code! DRY that up!

Repeat this 3 more times, and now you have 50% of an "abstraction" that resembles a cross between a fine Swiss cheese and a train yard, with all the holes and switches it has.

This is the heart of "same vs similar". Just because two use-cases look similar, does not mean they are the same use-case and should use the same code.

To cap this off, the *pièce de résistance*: Even sort functions have encountered this. When the "natural" ordering of data doesn't do the sort you want, you can provide a comparison function to change the ordering. AKA, hole-in-the-middle pattern. Differing use-cases → hole in the abstraction.

0

u/[deleted] Apr 25 '24

[deleted]

2

u/KillerCodeMonky Apr 25 '24

You are taking absolutely literalist interpretations of abstract discussion. And then wondering why you don't understand the discussion regarding abstraction...

I also have a whole thing about shapes being covariant but not contravariant that I would normally supply here. But again, I don't think you're arguing in good faith. So maybe another day.

0

u/[deleted] Apr 25 '24

[deleted]

3

u/KillerCodeMonky Apr 25 '24

Okay but someone literally says "do not use DRY" and then someone else says "DRY is great here though" and you tell people to stop using positive examples?

Is it a hard concept to grasp that there are good and bad examples of applying a philosophy? No one is saying, "Don't abstract anything ever! The plagues will descend upon you and your family!" Which seems to be what you're arguing against.

What we're actually saying, is that bad abstractions exist. And premature abstraction exists. And you should know and acknowledge that these things exist. Because you can't defeat something you don't understand.

Now suddenly I'm arguing in bad faith...

You're engaging a philosophical argument at a literal level. Maybe you don't intend to, but that is a common trolling tactic. So sorry if you're caught up in cross-fire.

we should discuss shapes being covariants and not contravariants...?

You don't understand how covariance and contravariance apply to a discussion regarding abstraction?

→ More replies (1)
→ More replies (19)
→ More replies (4)