20 ms·
Hang your code out to DRY
- recursivedoubts 5y agolike many older developers, I have become less ideological about DRY over time, particularly as I have seen it motivate extremely abstract solutions that have proven difficult to understand and maintain i have coined the term "Locality of Behavior" (LoB) as a competing design principle to DRY (as well as Separation of Concerns, SoC) that advocates more inlining of (potentially repetitive) logic in the interest of code maintenance and understandability: https://htmx.org/essays/locality-of-behaviour/ https://htmx.org/essays/locality-of-behaviour/ pic related: https://pbs.twimg.com/media/FAInxTuVkAATRrn?format=jpg&name=medium https://pbs.twimg.com/media/FAInxTuVkAATRrn?format=jpg&name=...
- darkwater 5y agoWhy does the "does it actually work" go down over time? I would expect to be the other way round: more experience, more chances your software "actually works", even if it's not following strict rules anymore.
- recursivedoubts 5y agoi interpret it this way: when I was younger it was really important to me that the code work the entire time I was writing it as I got older it got less important and now I make the code "look right" (that is, read well) and then I get it working
- edflsafoiewq 5y ago> more experience, more chances your software "actually works" Exactly, so you don't have to think so much about if it will work, you can take that for granted and think more about subtler things.
- contravariant 5y agoI'm about 80% sure that this graph eventually ends up back at the initial state.
- dahart 5y agoThere are multiple metrics to describe what “works” even means. You are right that battle tested code can be more stable and fulfill its goals more than new untested code or code written without a customer. At the same time it’s also true that any sizeable codebase, especially production code, will accumulate bugs over time, will almost always slow down over time, and will eventually once large enough become crufty and smelly and unfactorable and difficult and, maybe most importantly, expensive to maintain. Big systems are hard.
- MauranKilom 5y agoThe axis is labelled "relative importance". If you place non-zero value on readability, you must place less than 100% importance on "does it work".
- darkwater 5y agoWhy? There is absolute no correlation between readability and working status. It might be obfuscated code that works perfectly, or it might be obfuscated code that explodes every 2 days. And it might be perfectly clear code badly implementing a business need or implementing it perfectly.
- pastrami_panda 5y agoI really like WET (write everything twice). It also fits nicely with the rule of 3.
- timw4mail 5y agoI would only call code without proper abstractions WET (Winnow Everything Thrice). What acronym can we fit into "damp"?
- Jtsummers 5y agoDRY After Multiple Permutations
- contravariant 5y agoDon't add multiple parameters. Delete all multifunctional programs. Don't AMPlify code.
- marcus_holmes 5y ago+1 for Rule Of 3
- hcarvalhoalves 5y agoThe problem I’ve seen with the “rule of 3” in real life is that by the time someone is writing a similar implementation by the third time, 5 years have passed and the entire team has rotated, so the programmer doesn’t have enough context to DRY anymore, and then the failed “big refactor that breaks corner cases” happens. I find a mentality of _striving_ for DRY by default — even if end up choosing to duplicate for pragmatic reasons — to be beneficial in keeping programmers looking around for opportunities and refining the understanding of the context, with a better chance of incremental progress.
- Jtsummers 5y agoThat's not a problem with "rule of 3", it's a more general problem: Misunderstanding these "rules" and "principles". Specifically, trying to treat them as absolutes. "We must repeat ourselves three times to comply with 'rule of three'." Well, no, that's silly. It's a heuristic, a guideline, not a law that can never be broken. If you spot a clear case of real duplication even before creating the duplication (easier with experience, either total or with the system under development), then you can clean it up earlier. If you can't see the duplication or are uncertain about the duplication ("Is this real duplication, or just coincidence? Am I going to change 90% of the code after all the modifications are done or just one value which could become a parameter?"), then go ahead and repeat yourself. The same issue arises with YAGNI. People often jump too quickly to shouting YAGNI when the reality may be, and again this comes from experience-enabled judgment, that you are going to need it. "Don't make it a parameter, we aren't using it yet and don't know that we will." "But we do know, it's in the customer requirements that we don't hardcode the database server name everywhere, also it's just sensible." These rules, principles, guidelines, laws, or whatever term gets assigned to them are there to help. They are not there to be excuses to stop thinking, but to provide a structure around thinking and a way to discuss with other people. But that structure is not absolute, experience and judgement can lead to breaking any of these rules at any time based on the present situation. That present situation that only you (and your team) know, but not the people who discovered, created, or coined the rules. They can only offer advice and guidance and not absolute instruction.
- ebiester 5y agoMy first heuristic is: If I change the code in block A, is it assured that I will need to change the code in block B? My second heuristic is: Can I name the method I wish to de-duplicate in a way that is honest for all cases I wish to cover, yet explains its business purpose? The more it deviates from these heuristics, the more likely I am to duplicate the code in object oriented programming.
- piaste 5y agoI use, and teach juniors basically the same rule as your first one. I phrase it a little differently though: "if something happened in the future that required a change to function A, would the same requirement apply to function B as well?" I think your second heuristic is valid but a little dangerous, because being good at naming functions is somewhat orthogonal to being good at maintaining code.
- ebiester 5y agoFrom my perspective, naming functions well is a key component of maintainable code. However, in this case, I use it is an extension of the question "is this the same concept?" if it looks like useFloorWaxOrDessertTopping, it's a good clue that you may have the same lines of code but they are certainly not the same concept.
- terrortrain 5y agoI wish I saved the source, but I saw somewhere the term: "Don't repeat concepts" While it doesn't spell out a word, I think it's much better advice than DRY, and better aligns with your heuristics.
- chrisweekly 5y agoRelated tangent: "AHA" (Avoid Hasty Abstractions) is a decent counter to the over-application of DRY. IME, people reach for DRY too quickly, at the expense of other worthy but more subtle principles.
- ChrisMarshallNY 5y agoI find it fascinating that people are so against inheritance/polymorphism, these days. That's one of the absolute best ways to DRY. factoring out common base classes is a classic OO exercise. It's possible to drastically reduce the size of a codebase, and the potential error exposure, by doing some simple extractions.
- activitypea 5y agoTo me, inheritance and polymorphism are two different things. Polymorphism is about different units implementing an interface or equivalent protocol and that rocks. Inheritance is, essentially, dumping a bunch of code into your new class, and most of the time just imposes constraints and breaks API boundaries for no good reason. After studying and doing OOP for about 5 years, I don't see the advantages of inheritance over composition. The only value I see is libraries exposing base classes that enforce behavior on user-written subclasses, stuff like React's Component or Java's HttpServlet. Seems to me we can have polymorphism without subclassing as long as the programming language has a half-decent type system.
- chrisweekly 5y agoYeah; "favor composition over inheritance" remains good advice. "Classical" OO inheritance is brittle and often harmful.
- ChrisMarshallNY 5y agoWell, this is one of those "yes and no" situations. The biggest argument that I hear against OO, is that "someone may misuse or misunderstand it." I feel that this reflects the tech industry's obsession with hiring armies of relatively inexperienced developers, and then cycling through them, because we don't do what it takes to retain people. I like composition. I use it often. It is not a "one size fits all" solution to anything; just like OO isn't. "Reduce state" is another big rallying cry. Good advice, for algorithms, multithreaded service providers, and engines. Not so good, for UI, and, in many cases, device control. I have spent the last couple of days, working on the login screen for the app I'm developing. It's loaded with state. That can't be avoided, and negotiating the several different states that this -seemingly- innocuous screen can have, is not for the faint of heart, but it needs to be done right, because it's the first screen our users see. It also optionally implements Sign In With Apple, which brings its own baggage. The users' experience must be absolutely frictionless, while also being very secure. The work has involved the server (PHP), the SDK (Swift), and the app, itself (also Swift). I'm not done. I keep uncovering corner cases. I'm just not a fan of "Don't use X, because X is bad, and you're a bad programmer, if you use X." The tech industry has been dealing with this, since the GOTO wars. Most of my projects are a hideous chimera of decades-old techniques, mixed with cutting edge stuff. If someone wants to work on it, then they need to have their stuff together. I'm not going to "dumb it down," but I need to do a lot of documentation (I write about how I document, here: https://littlegreenviper.com/miscellany/leaving-a-legacy/ https://littlegreenviper.com/miscellany/leaving-a-legacy/). Here's an interesting thing that happened to me, some time ago, and I decided to write about it: https://littlegreenviper.com/miscellany/swiftwater/the-curious-case-of-the-protocol-default/ https://littlegreenviper.com/miscellany/swiftwater/the-curio...
- cjfd 5y agoIf there is one single article about programming that I positively hate it is 'duplication is better than the wrong abstraction'. As the Jason Swett article points out the article seems to install a sort of fear of refactoring. If there is a 'wrong abstraction' nobody will every change it and now we are doomed to live with this wrong abstraction for all of eternity. The wrong abstraction can be turned into the right abstraction or can be undone if it is really not going anywhere. If that is what is happening at least people are trying to improve the code and if people try something it will eventually work. In many cases a bad code base is difficult to change because it is wrong in so many respects that it is difficult to tell where to start. If there is an attitude of refactoring and improvement things that are bad can be taken out quickly. Now, one should, of course not be stupid about removing duplication. If two functions just look vaguely similar but this is more of a coincidence than something that occurs because of the nature of the problem that these two functions are solving then they should absolutely not be one abstraction.... I suppose one might need to point that out to some developers but certainly not to ones who have been developers for some time and who actually have some talent as developers.
- horsawlarway 5y agoBad abstractions tie together components that shouldn't have been tied together. Too many bad abstractions are how you quickly end up with that "Bad code base" that you believe is difficult to change - Things are wrong because they're tied together in ways that don't actually make sense, and changing code to support refactoring one use-case creates a wave of cascading changes to other places those abstractions are touched/consumed. If you miss one, or forget an edge case, or have a skimpy test suite - suddenly that refactoring you're so keen on is what's introducing new things that are "wrong" - because they shouldn't have been tied together but were, and you don't understand or remember all of the edge cases. Basically - My rebuttal is this: It's very easy to refactor a codebase with duplication and introduce an abstraction for the current behavior. It's very HARD to refactor a codebase riddled with abstractions that shouldn't be there. This means that by default - abstractions should only be introduced very carefully. Refactoring is fine, but you're paying more to refactor a bad abstraction than to refactor duplication. Good programmers understand that most of their value isn't in what their code looks like - it's in what it does for the users. Duplication can feel dirty, and it tends to trigger a "puzzle game" mentality in a lot of programmers, who want to fit the pieces together to make it pretty. AVOID THIS INSTINCT.
- sfvisser 5y agoAbstraction is inherently not about deduplication, it is about capturing intent and meaning. About the universality of certain concepts within your code base. Either based on your problem domain or in the context of your application architecture. Once you abstract only to shorten your code you’ll likely regret it quickly.
- deleted 5y ago[deleted]
- AnimalMuppet 5y agoBut DRYing your code can cause it to shrink...
- D-Coder 5y agoThat's a good thing.
- bcrosby95 5y ago> On re-reading Sandi’s original article it says kind of what I remember it saying, but it also… kinda doesn’t? There’s a lot more talk about programmers honoring the abstractions of elders who came before them That's because the original article is so clearly about tearing down bad abstractions, but a large majority of programmers - based upon discussion about the article - seem to never get past the first part of it. Given a long enough time horizon, all abstractions turn bad. The solution isn't to not abstract. The solution is to tear them down when they go bad. And if you don't learn to tear down bad abstractions, your codebase will devolve into shit regardless of what you do.
- cogman10 5y ago> Given a long enough time horizon, all abstractions turn bad. The solution isn't to not abstract. The solution is to tear them down when they go bad. I disagree with this analysis. Abstractions certainly go bad, but I don't think it's correct to say all abstractions go bad. The solution I took from Sandi's post was two-fold. * Don't prematurely abstract, it's better to live with a little duplication vs aggressively eliminating it. * Don't hold abstractions sacred, when you start seeing an abstraction with too many conditionals, consider breaking apart the use-cases to see if there are actually 2 distinct abstractions happening.
- Zababa 5y agoThat may be projection on my part, but I feel like lots of programmers (me included, of course) have a hard time accepting code as a living thing, and would rather build something that "lasts forever". I feel like this is the kind of thinking that pushes us to try to make abstractions that cover all of the cases, spend lots of time on things with little value (in a business context) to "make it right", flaws like that. Reading the "original SOLID paper" [1] was enlightning to me. The initial assumption is that software rots, or gets less flexible with time. The best way to prevent that is to identify the part that is the least flexible/most rotten and replace it. But for that you need two things: being able to clearly identify parts of the software, and being able to replace them. This is where modularity and abstractions comes in. But this is also where the good old delete key comes in. This is where modularity and abstractions comes in. Building software from parts, expecting to replace them does leads to abstractions, but different ones from building software expecting it to never be replaced. And, in my opinion, the first kind is easier to deal with. [1]: https://web.archive.org/web/20150906155800/http://www.objectmentor.com/resources/articles/Principles_and_Patterns.pdf https://web.archive.org/web/20150906155800/http://www.object...
- manuel_w 5y agoI once read a comment I wish I'd saved. It goes along the lines of: W beats X, X beats Y, Y beats Z; in terms of what principle you'd like to apply to your code. One of these letters was essentially representing DRY. I summed things up pretty nicely. Does someone happens to remember?
- isleyaardvark 5y agoI'm willing to bet it was this, because I was so struck by it I saved it: >I try to optimize my code around reducing state, coupling, complexity and code, in that order. I'm willing to add increased coupling if it makes my code more stateless. I'm willing to make it more complex if it reduces coupling. And I'm willing to duplicate code if it makes the code less complex. Only if it doesn't increase state, coupling or complexity do I dedup code. https://news.ycombinator.com/item?id=11042400 https://news.ycombinator.com/item?id=11042400 edit: and of course it's from a long and worthwhile HN thread on Sandi Metz's original article which was the start of the back-and-forth resulting in the article for this thread
- manuel_w 5y ago> I'm willing to bet it was this, because I was so struck by it I saved it: This is indeed what I was looking for. I too was struck by it. Thank you SO much.
- 0xbadcafebee 5y agoCan't we all just agree that none of these solutions are perfect and you might actually have to change what you do depending on the circumstance?
- ramoz 5y agoDuplication can be better than integration.