← Back to context

Comment by AussieWog93

10 hours ago

DRY is my pet example of this.

I've seen CompSci guys especially (I'm EEE background, we have our own problems but this ain't one of them) launch conceptual complexity into the stratosphere just so that they could avoid writing two separate functions that do similar things.

I think I remember a Carmack tweet where he mentioned in most cases he only considers it once he reaches three duplicates

  • The "Rule of 3" is a pretty well known rule of thumb; I suspect Carmack would admit it predates him by a fair bit.

  • I once heard of a counter-principle called WET: Write Everything Twice.

  • Why 3? What is this baseball?

    Take the 5 Rings approach.

    The purpose of the blade is to cut down your opponent.

    The purpose of software is to provide value to the customer.

    It's the only thing that matters.

    You can also philosophize why people with blades needed to cut down their opponents along with why we have to provide value to the customer but thats beyond the scope of this comment

    • Why baseball? You don't use the number 3 in any other context?

      If you write a lot of code, the odds of something repeating in another place just by coincidence are quite large. But the odds of the specific code that repeated once repeating again are almost none.

      That's a basic rule from probability that appears in all kinds of contexts.

      Anyway, both DRY and WET assume the developers are some kind ignorant automaton that can't ever know the goal of their code. You should know if things are repeating by coincidence or not.

    • Rule of 3 will greatly benefit the customer over time as it lessens the probability of bugs and makes adding new features faster.

    • "The purpose of software is to provide value to the customer."

      Partially correct. The purpose of your software to its owners is also to provide future value to customers competitively.

      What we have learnt is that software needs to be engineered: designed and structured.

      6 replies →

I worked for a company that also had hardware engineers writing RTL. Our software architect spent years helping that team reuse/automate/modularize their code. At a mininum, it's still just text files with syntax, despite rather different semantics.

I've heard that story a few times (ironically enough) but can't say I've seen a good example. When was over-architecture motivated by an attempt to reduce duplication? Why was it effective in that goal, let alone necessary?

  • I think there is often tension between DRY and "thing should do only one thing." E.g., I've found myself guilty of DRYing up a function, but the use is slightly different in a couple places, so... I know, I'll just add a flag/additional function argument. And you keep doing that and soon you have a messed up function with lots of conditional logic.

    The key is to avoid the temptation to DRY when things are only slightly different and find a balance between reuse and "one function/class should only do one thing."

    • For sure. I feel I need all of my experience to discern the difference between “slightly different, and should be combined” and “slightly different, and you’ll regret it if you combine them.”

      One of my favorite things as a software engineer is when you see the third example of a thing, it shows you the problem from a different angle, and you can finally see the perfect abstraction that was hiding there the whole time.

  • Buy me a beer and I can tell you some very poignant stories. The best ones are where there is a legitimate abstraction that could be great, assuming A) everyone who had to interact with the abstraction had the expertise to use it, B) the details of the product requirements conformed to the high level technical vision, now and forever, and C) migrating from the current state to the new system could be done in a bounded amount of time.

    My view is over-engineering comes from the innate desire of engineers to understand and master complexity. But all software is a liability, every decision a tradeoff that prunes future possibilities. So really you want to make things as simple as possible to solve the problem at hand as that will give you more optionality on how to evolve later.

  • I’ll give a simplified example of something I have at work right now. The program moves data from the old system to the new system. It started out moving a couple of simple data types that were basically the same thing by different names. It was a great candidate for reusing a method. Then a third type was introduced that required a little extra processing in the middle. We updated the method with a flag to do that extra processing. One at a time, we added 20 more data types that each had slightly different needs. Now the formerly simple method is a beast with several arguments that change the flow enough that there are a probably just a few lines that get run for all the types. If we didn’t happen to start with two similar types we probably wouldn’t have built this spaghetti monster.

  • IMHO, it comes down to awareness/probability about the need to future proof or add defensive behavior.

    The spectrum is [YAGNI ---- DRY]

    A little less abstract: designing a UX comes to mind. It's one thing to make something workable for you, but to make it for others is way harder.

  • I saw a fancy HTML table generator that had so many parameters and flags and bells and whistles that it took IIRC hundreds of lines of code to save writing a similar amount of HTML in a handful of different places.

    Yes the initial HTML looked similar in these few places, and the resultant usage of the abstraction did not look similar.

    But it took a very long time reading each place a table existed and quite a bit longer working out how to get it to generate the small amount of HTML you wanted to generate for a new case.

    Definitely would have opted for repetition in this particular scenario.

DRY is misunderstood. It's definitely a fundamental aspect of code quality it's just one of about 4 and maximizing it to the exclusion of the others is where things go wrong. Usually it comes at the expense of loose coupling (which is equally fundamental).

The goal ought to be to aim for a local minima of all of these qualities.

Some people just want to toss DRY away entirely though or be uselessly vague about when to apply it ("use it when it makes sense") and thats not really much better than being a DRY fundamentalist.

  • DRY is misnamed. I prefer stating it as SPOT — Single Point Of Truth. Another way to state it is this: If, when one instance changes in the future, the other instance should change identically, then make it a single instance. That’s really the only DRY criterion.

    • I like this a lot more, because it captures whether two things are necessarily the same or just happen to be currently the same.

      A common "failure" of DRY is coupling together two things that only happened to bear similarity while they were both new, and then being unable to pick them apart properly later.

      1 reply →

    • I said this elsewhere in the comments, but I think there's sort of a fundamental tension that shows up sometimes between DRY and "a function/class should only do one thing." E.g., there might be two places in your code that do almost identical things, so there's a temptation to say "I know! I'll make a common function, I'll just need to add a flag/extra argument..." and if you keep doing that you end up with messy "DRY" functions with tons of conditional logic that tries to do too much.

      Yeah there are ways to avoid this and you need to strike balances, but sometimes you have to be careful and resist the temptation to DRY everything up 'cuz you might just make it brittler (pun intended).

    • That’s how I understand it as well. It’s not about an abstract ideal of duplication but about making your life easier and your software less buggy. If you have to manually change something in 5 different places, there’s a good chance you’ll forget one of those places at some point and introduce a bug.

    • Yes, and there are many different kinds of truth, so when two arise together—we can call this connascence—we can categorize how these instances overlap: Connascence of Name, Connascence of Algorithm, etc.

    • That's how I understood it. If you add a new thing (constant, route, feature flag, property, DB table) and it immediately needs to be added in 4 different places (4 seems to be the standard in my current project) before you can use it, that's not DRY.

      2 replies →

    • Renaming it doesnt change the nature of the problem.

      There should often be two points of truth because having one would increase the coupling cost more than the benefits that would be derived from deduplication.