Live data from Hacker News

DRY is an over-rated programming principle?

gordonc.bearblog.dev

491–500 of 501 posts

Re: DRY is an over-rated programming principle?

#491
The problem with dry is no one tells when when to not use it. It's great when you have 5 instances of the same string, much like oop is great when all your objects are animals that fit into a neat little category. It's not so great when you're drying code across feature boundaries. Features tend to diverge over time rather than converge so what's dry one day is garbage the next. You refactor the code to be dry so 3 features are now one function and someone comes along asking for an amendment to one of those 3 features which adds an edge case to your function. Now you've broken another feature because you didnt check if your new edge case would change other features but quality control didn't check the other features cause no one asked for that feature to be changed and how do they know that it's all one function under the hood. Now you have bugs in production and no one to cover your ass. Time goes on and you add more edge cases and now you have one function that does many things with special edge cases throughput it. You cant separate one feature without interacting with another, almost like you're interacting with strands of spagetti and you can't help but pick up a bunch when you only really wanted one noodle.

Tldr if you're making code dry and you insist that you make code dry across feature boundaries then for the love of god make unit tests for those functions. Or keep your functions dry and your features wet.

Re: DRY is an over-rated programming principle?

#492
post #434

Earlier quoted context omitted.

I'm not sure I follow... Can you provide an example? (junior dev here) If I understand some of it correctly, I was contemplating this when I started writing functions for "single functional concepts" like, "check for X; return true or false", then called each of those functions sequentially in a single "run" function. Is that what you mean? I found that approach much easier to test the functions and catch bugs, but y…

Specifics might depend on the language, domain, and team (and individual preference), so it's hard to avoid being general. I would say some junior devs can get too fixated on hierarchies and patterns and potential areas of code re-use, though; instead, they should try to write code that addresses core problems, and worry about creating more correct abstractions later. Just like premature optimization is the "root of…

I'll start out by saying I have some pretty strong positions opposing what it sounds to me like yours are.

>I would say some junior devs can get too fixated on hierarchies and patterns and potential areas of code re-use, though;

Agreed.

> instead, they should try to write code that addresses core problems,

Still with you, agreed.

> and worry about creating more correct abstractions later.

I don't quite agree. I agree you shouldn't spend too much time, but I think "don't worry about it" gets you a hodgepodge of spaghetti code and half-baked ideas that no one can maintain in the future.

One of the most important things someone can do when adding a feature for instance is understanding the surrounding code, it's intentions, and how any abstractions it may have work, and then add their feature in a way that complements that and doesn't break backwards compatibility.

I'd go as far as arguing that only ever MVP'ing every story without a thought to design or abstraction is one of the major problems in industry alongside cargo-culting code maintenance rather than ever trying to form deep understanding of any meaningful part of the software.

> Just like premature optimization is the "root of all evil", the same goes for premature refactoring. This is the rule of YAGNI: You Ain't Gonna Need It. Don't write code for problems you think you might have at some indeterminate point in the future.

YAGNI is far too prescriptive and misses the point of programming that literate programming gets right:

Programming (like writing) is about communicating intent to other human beings in an understandable way and shuffling complexity around in the way it makes the most sense for:

- The typical reader skimming to understand something else (Needs to know: what does it do?) - The feature adder (needs to know how it works, so they need a high level, then easy way to understand the low level as needed) - The deep reader (needs to be able to take in all of the code to deeply understand it, needs straightforward path to get there)

What you describe sounds like no abstraction and just throwing all of the complexity in front of everyones faces all at once. I can appreciate the habitability advantage of that, but I think that discarding context of acquired domain knowledge as you work on issues is too great of a cost.

In case it's not obvious, the words came to me for the point I'm trying to make: Domain knowledge you acquire while working on something should be encoded in sensible abstractions that others can uncover later on, peeling away more and more complex layers as needed.

> When it comes to testing, TDD adherents will disagree, but if you ask me it's overkill to test small private subroutines (or even going so far as to test individual lines of code). For example, if I have a hashing class, I'm just going to feed the hash's test vector into the class and call it done. I'm not going to split some bit of bit-shift rotation code off into a separate method and test only that; if there's a bug in that part, I'll find it fast enough without needing to give it its own unit test. That's what debuggers are for. All the unit test should tell me is whether I can be confident that the hashing part of my code is working and won't be the cause of any bugs up the line.

This view sounds like it may be a direct result of a YAGNI/avoid abstraction style to me actually. If you avoid abstracting or code-reuse quite a lot (or even don't spend enough energy on it), you lose one of the largest benefits of TDD:

regression testing

If nearly all of your functions are single use or don't cross module boundaries... then the value add of TDD's regression testing never really has a chance to multiply.

For OOP I feel like this would be reflected in terms of testing base objects the most or static methods. For functional code, it would just be shared functions.

> Obviously I'm not in the "tests first" camp; instead I write tests once a class is complete enough to have a clearly defined responsibility and I can test that those responsibilities are being fulfilled correctly

I'd argue you are testing in your head anyway. Visualizing, conceptualizing, and trying to shape the essence of the problem into something that makes sense.

The problem is sometimes our mental compilers/interpreters aren't perfect and the mistakes are reflected as kludges or tech debt in our code.

Re: DRY is an over-rated programming principle?

#493
post #188

A better formulation of DRY is SPOT (Single Point Of Truth). Definitions (code, data) that represent the same “truth”, i.e. when one changes all have to change to represent a consistent truth, should be reduced to a single definition. For example, if there is a rule that pizzas need at least one topping, there should only be a single place where that condition is expressed, so that when the rule changes, it isn’t jus…

Partially agree. I think SPOT (I'd always heard it called single source of truth) is a more universally applicable paradigm than DRY. Having said that, the cost of creating dependency chains is often underestimated. Overly dogmatic adherence to SPOT/SST can lead you to make the wrong tradeoff on coupling two unrelated areas of your codebase to unify some trivial truth. I'd also say there is a lot of nuance about what…

I really like your SPOT better than SSoT, the acronym is so much more on point, really hits the "spot"

Re: DRY is an over-rated programming principle?

#494
Here's how I apply DRY.

As I build a thing, I just sling duplicate code like I'm getting paid by the line. Once I've created all or most instances of the duplicate code, and they're working, only then do I circle back refactor it to extract common concepts and abstractions.

I've learned over the years that I don't really understand what I'm building until it's built. I need to step back and look at the patterns that have formed to spot the difference between firm concepts and trivial duplication.

Doing this well takes experience. It involves predicting how your code is likely to change. One lesson from experience is to favor repetition over bad abstractions. I formed this opinion from living through the pain both types of anti-patterns. Duplication causes the need to find, change and test all instances. That sucks. It leave you open to bugs. But bad abstractions can require ripping the whole thing apart.

Re: DRY is an over-rated programming principle?

#495
post #290

Earlier quoted context omitted.

I regularly see code of the form `if(x == false)` as the author has a distrust of `if(!x)`. I guess the author just distrusts smaller things, leaving me to distrust the author’s larger things.

This is completely another level of "issues" than other problems in this thread. During code review I'd only mention it as a nit. The longer form is correct and the only downside is that's a bit longer. It doesn't mess up code modularity or affect maintainability in a noticeable way. Well, assuming that the language doesn't have any quirks in this area - e.g. in Java your statements aren't equivalent for a Boolean x.

Nitpick (as in general you are right regarding java as well): I’m fairly sure they are the same for java in this instance. Both will convert Boolean to boolean, throwing an NPE if it was a null.

Re: DRY is an over-rated programming principle?

#497
post #495

Earlier quoted context omitted.

This is completely another level of "issues" than other problems in this thread. During code review I'd only mention it as a nit. The longer form is correct and the only downside is that's a bit longer. It doesn't mess up code modularity or affect maintainability in a noticeable way. Well, assuming that the language doesn't have any quirks in this area - e.g. in Java your statements aren't equivalent for a Boolean x.

Nitpick (as in general you are right regarding java as well): I’m fairly sure they are the same for java in this instance. Both will convert Boolean to boolean, throwing an NPE if it was a null.

Ha, that's true, thanks! I guess my Java-fu is weak these days :)

Re: DRY is an over-rated programming principle?

#498

Earlier quoted context omitted.

It's been awhile since I absorbed the weird programming norm that "real programmers use the !x form!" but even after 10+ years of !x , I still find ==false more readable.

Even if the not operator in the language you're writing in happens to be the actual word 'not' ?

My whole career has been C++ and shader languages, so this really hasn't come up for me. I imagine it being a real word would improve readability greatly.

Re: DRY is an over-rated programming principle?

#499

Earlier quoted context omitted.

It's data. make_pizza(toppings=HAWAIIAN_TOPPINGS) or make_pizza(HAWAIIAN) or similar. Data should generally not be hard-coded, both because it changes and because it wants to be validated. Starting with: HAWAIIAN = { TOPPINGS: [ PINEAPPLE ... is okay. That can later be loaded from a config file, a database, or otherwise, as the system expands.

There's an interesting architectural decision here: what form of the pizza recipes database strikes the right balance between too hardcoded and too complex. I'd use some kind of configuration file or RDBMS, constants are more readable but still out of place as part of code.

The nice thing about constants is that you can't make typos. A string like "peperoni" isn't caught, but toppings.PEPERONI will fail immediately.

I use Python. It's easy enough to, for example, make an `enum` from entries in a config file or even a database:

https://docs.python.org/3/library/enum.html

Scroll down to the functional API. There are many similar design patterns. The nice thing about these is that you get automatic type checking. If your config file is:

    toppings: ['pepperoni', 'ham', 'pineapple'],
    pizzas: {
       'Hawaiian': ['pinapple']
    }
If you load this in as strings, it will load and later silently fail. If you add validation code, you'll only validate what you remember. If you make an enum or similar custom type, it necessarily will fail-on-load, and probably with a reasonable error.

The major downside -- which is really incidental (due to poor library design) -- is that most JSON/YAML libraries won't reasonably serialize/deserialize non-Python types. So there's a bit of (unnecessary) overhead there.

Re: DRY is an over-rated programming principle?

#500

Earlier quoted context omitted.

Subtle bug in that toppings has a mutable default argument [1]. [1] https://docs.python-guide.org/writing/gotchas/

There is no bug... yet. Unless you modify the default argument. Sometimes I just want to monkeypatch the list to be immutable in my app.

I'd consider this a bug and not expected behavior.

  def make_pizza(crust=THIN, toppings=[], cheese=REGULAR, sauce=TOMATO):
    // first call: toppings=["peperoni"]
    // second call: toppings=["peperoni", "sausage"]
    // third call: toppings=["peperoni", "sausage"]

  make_pizza(toppings=["peperoni"])
  make_pizza(toppings=["sausage"])
  make_pizza()
Post reply on HN