[HN Gopher] Hang your code out to DRY
___________________________________________________________________
Hang your code out to DRY
Author : hejsna
Score : 64 points
Date : 2022-01-10 15:29 UTC (2 days ago)
(HTM) web link (johan.hal.se)
(TXT) w3m dump (johan.hal.se)
| cjfd wrote:
| If 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 wrote:
| Bad 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.
| cogman10 wrote:
| Agree completely.
|
| I've dealt with too many code bases that didn't follow this
| advice. It's extremely expensive to cut down capabilities
| from code because they are overly coupled.
|
| DRY tends to create things like utility classes and deep
| dependency trees. For example, I saw a non-ui code base that
| pulled in JavaFX to use their pair class.
| [deleted]
| ChrisMarshallNY wrote:
| I 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 wrote:
| To 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 wrote:
| Yeah; "favor composition over inheritance" remains good
| advice. "Classical" OO inheritance is brittle and often
| harmful.
| ChrisMarshallNY wrote:
| Well, 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/).
|
| 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-
| curio...
| cogman10 wrote:
| While inheritance can certainly work, especially in terms
| of UI development, the issue is when you are talking more
| abstract concepts. At that point, it can become really
| hard to figure out the right lines for what should be
| inherited vs composed.
|
| In my experience, poorly composed code is simply easier
| to understand than code which poorly applies inheritance.
|
| For me, inheritance is best used lightly. The obvious
| smell is when you end up with methods that don't apply to
| all the base classes. Or, said another way, when a super
| class it has a superset of capabilities for a base class.
|
| A good example of this is Java's collections, which, for
| the most part, are quite good with their inheritance.
| However, because the base classes have mutable methods,
| it makes it a pain to deal with unmodifiable collections.
| Java's mistake is they should have had the default
| collection be unmodifiable and had sub classes which
| added mutation capabilities. List and MutableList, for
| example.
| twblalock wrote:
| > 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.
|
| That is true (and it won't change, so we need to behave
| like it won't...), but that is not the only reason.
|
| An inheritance hierarchy in a large program that makes
| sense today might not make sense in a year or two.
| Refactoring it is hard. Refactoring a composition-based
| solution is easier.
|
| Composition-based solutions are also easier to test:
| inject mocks. It's a lot easier to mock a compositional
| dependency than it is to mock behavior that's inherited
| from a parent class.
|
| Given that composition is easier to maintain and test,
| and that it can achieve the same functionality as
| inheritance, I've pretty much stopped using inheritance.
| And I write Java 99% of the time.
| ChrisMarshallNY wrote:
| I write Apple UIKit apps. I am looking forward to using
| SwiftUI, which is designed to afford use of Protocol-
| Oriented Programming, reactive/observer stuff, and a lot
| of lower-state stuff. I think it would have made the work
| I've been doing in the last few days, _much_ easier.
|
| But if you use UIKit, then it's fairly important to use
| classic MVC (not MVVM), as that is what the SDK was
| specifically designed for. Trying to coerce it into other
| models just causes a lot of extra pain and complexity.
|
| Also, there are models that have been specifically
| designed (I won't talk about which), to introduce extra
| complexity. These are made to allow a design to be
| "broken up," so that parts can be assigned to different
| developers.
|
| _> and it won 't change, so we need to behave like it
| won't..._
|
| I sincerely hope that you're wrong. It's been an
| unmitigated disaster.
| manmal wrote:
| It's possible (and IMO enjoyable) to write UIKit apps
| with ComposableArchitecture, a purely value-typed
| approach. I've also mostly used MVVM with UIKit since
| 2015, and found my apps to be stabler and easier to
| maintain than standard MVC apps.
|
| In projects I lead I've been discouraging inheritance
| because I've found it highly detrimental to
| maintainability. "Base" classes invite bloat and
| overgeneralization, especially with inexperienced devs.
| ChrisMarshallNY wrote:
| Isn't TCA a dependency framework?
|
| I haven't found much joy in MVVM. I haven't found that it
| provides enough benefits to spend the time switching
| over.
|
| I am looking forward to SwiftUI. I really hope that the
| documentation improves, though.
| manmal wrote:
| TCA is a Redux/Elm-style data and state management lib
| that aspires to handle an app's complete business logic,
| preventing devs from mixing the logic into the UI layer
| via SwiftUI's @State and @StateObject. And contrary to
| Redux, it's completely type safe and aware of deep
| component nesting (sub reducer states and actions are
| "pulled back" into their parent reducer pendants), so it
| allows truly composable components.
|
| If you haven't checked out PointFree's content (authors
| of TCA among many other things), it's one of the best
| resources on Swift and algorithms for sure.
| jamil7 wrote:
| > Isn't TCA a dependency framework?
|
| It is but it's quite straightforward to build your own
| simpler version using only Combine - that's what we're
| doing at the moment and it tends to work very well,
| interacting with Core Data has been kind of clunky
| though.
|
| > I am looking forward to SwiftUI. I really hope that the
| documentation improves, though.
|
| I've been on a project using it for the last 8 months
| both for macOS and iOS, I would honestly not rush into
| using it anytime soon. It's improved significantly since
| release, especially if you can target the newest iOS but
| requiring that is a hard-sell for most. On the Mac it's a
| different story and is much more neglected, macOS
| versions also tend to have far longer tails than iOS so
| it's a non-starter to try and sell a Monterey-only Mac
| app.
|
| Edit: rereading my comment it sounds like I'm really down
| on SwiftUI - which I'm not, I think it's great when it
| works but there's a lot of rough edges at the moment and
| also quite a lot of hype.
| manmal wrote:
| If you are using TCA only for dependency management, I'd
| suggest to reconsider and put all business logic in it.
| In my experience, most serious efforts of wrangling
| SwiftUI into a complex app wind up with a solution that's
| similar to TCA.
| jamil7 wrote:
| Ah! We're putting all our business logic in it, I guess I
| misunderstood what OP was asking and assumed they meant
| something else, that's why I mentioned we built our own
| version of it internally.
| ChrisMarshallNY wrote:
| Thanks, both of you, for the explanations.
| jcelerier wrote:
| > After studying and doing OOP for about 5 years, I don't see
| the advantages of inheritance over composition.
|
| I mean, "prefer composition over inheritance" was in the GoF
| book which is from 1994, and is one of the core OOP books. If
| you've just been studying OOP for five years, it's likely
| that you weren't even BORN when this was already industry-
| level good practice.
| activitypea wrote:
| Universities in my country are hopelessly stuck in the
| early 90s :)
| hansvm wrote:
| I think a lot of people have been burned by inheritance done
| wrong.
|
| With a lot of other techniques, if the implementation isn't
| perfect and you don't own the library you're using you still
| have a lot of power available to patch things up and make
| everything play nicely together. Inheritance mixes
| implementation details with your type checking, so in a lot of
| languages that can make it extremely painful to turn a body of
| almost-good-enough code into something that's actually usable.
| contravariant wrote:
| There's something to be said for preferring composition over
| inheritance, but in that case overly DRY code consists almost
| entirely of glue. As with almost anything it's a matter of
| choosing the right tool for the job.
|
| The only code style advices that I've found to hold nigh
| universally are the following:
|
| - The best code is no code
|
| - Don't end classes in 'er' or 'or'
|
| Coding paradigms are good when they let you do those things and
| are bad when they don't do both of those things (i.e. they
| result in more code or clases ending in 'er'; a class named
| 'Helper' is a code smell worse than sulfur dioxide)
| ChrisMarshallNY wrote:
| Almost every one of my classes (in my UIKit app) is a
| ViewController. I don't really need to write anything more.
|
| So they pretty much _all_ are "er" classes. -\\_(tsu)_/-
| klyrs wrote:
| Simple fix, name it HelperClass instead?
| magicalhippo wrote:
| You don't have to use OO though. We're using an OO-oriented
| language at work, but I do a lot of de-duplication of code by
| extracting common functions.
|
| If needed I can delegate the specialization to an interface or
| in a function reference parameter (so anonymous functions can
| be passed) rather than in a subclass.
|
| When implementing the interfaces I might use subclasses though,
| if I have some very similar variants.
| bschwindHN wrote:
| I don't think people are necessarily against polymorphism or
| factoring out common logic, but they're definitely against big
| class hierarchies in the old "OO" style.
|
| Plain data structures with polymorphism via
| traits/interfaces/protocols seems like it's becoming the more
| popular way to handle these problems (I have no way to prove
| this, of course), and I prefer that way as well.
|
| It's just the worst though when a dev sees two chunks of code
| that happen to be "shaped" the same and decide to tie them
| together with one abstraction. It's like seeing two different
| cables in a building that carry radically different signals but
| happen to go along the same path for awhile, and someone comes
| along and zip ties them together. You may have wanted to be
| able to route one of them completely differently, or maybe you
| wanted to add one more of the same cable, and now you have to
| cut up all the zip ties and either re-apply them (shove it into
| the existing abstraction), or bundle them up some other new
| way.
| ryanbrunner wrote:
| Base classes tend to limit what your abstractions are though.
| In practice I've found that while different classes share
| common behaviour, it's not always so straightforward that it
| can be arranged into a tree. A mixin type approach, where
| behaviours can be attached to different classes (particularly
| if those behaviours don't define any new state) works much
| better.
| rimliu wrote:
| I came to conclusion that very few people actually understand
| what a good OO design is and ever fewer have skills to
| implement it. All this talk about SOLID patterns and what is
| more likely to result in overengineered monstrosity than in
| something elegant and easy to maintain.
| ChrisMarshallNY wrote:
| One way to understand OO, is to write OO code in a non-OO
| language (like C). When you need to basically write your own
| vtables, you really understand how things work.
|
| I had to do that, in the early '90s, because I was writing an
| SDK that had to be implemented in C, but used object
| abstraction. It ended up being used for over 25 years.
| marcus_holmes wrote:
| Because a lot of us learned the hard way that inheritance can
| very easily lead to a dark place. Changing behaviour on a
| grandparent object in specific circumstances related to the
| implementation of a child object gets really nasty.
| pastrami_panda wrote:
| I really like WET (write everything twice). It also fits nicely
| with the rule of 3.
| dllthomas wrote:
| I really don't like either "the rule of 3" or WET because I
| think they take the focus off the part of DRY that matters. It
| shouldn't be about syntactic repetition, but (as originally
| stated) about repetition of pieces of knowledge.
|
| The focus on syntax misleads in two ways. First, it sometimes
| motivates consolidation of things that are merely
| coincidentally syntactically similar (pushing back on this is
| most of where WET and Ro3 are useful, when they are), which
| makes it harder to update the code when one "piece of
| knowledge" needs updating but not the other(s). If you have 10
| things that all happen to be identical today, but any of them
| might change independently in any direction, combining them
| isn't "more DRY".
|
| Second, the same piece of knowledge may be repeated in
| different syntax. If your setup means you have to say "there's
| a button here" one way in HTML, another way in JS, and another
| way in CSS, then combining those would be "more DRY" even
| though no syntax repeats. This isn't to say that combining
| those (say through code generation?) would necessarily be
| better - DRY is a guideline to be balanced against other
| guidelines; but I do assert that it is a more useful guideline
| when we think in terms of duplicated knowledge than in terms of
| duplicated syntax.
| hcarvalhoalves wrote:
| The 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 wrote:
| That'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_.
| marcus_holmes wrote:
| +1 for Rule Of 3
| timw4mail wrote:
| I would only call code without proper abstractions WET (Winnow
| Everything Thrice). What acronym can we fit into "damp"?
| Jtsummers wrote:
| DRY After Multiple Permutations
| contravariant wrote:
| Don't add multiple parameters.
|
| Delete all multifunctional programs.
|
| Don't AMPlify code.
| AnimalMuppet wrote:
| But DRYing your code can cause it to shrink...
| sfvisser wrote:
| Abstraction 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.
| bcrosby95 wrote:
| > 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.
| Zababa wrote:
| That 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.object...
| cogman10 wrote:
| > 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.
| 0xbadcafebee wrote:
| Can'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?
| ebiester wrote:
| My 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 wrote:
| I 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 wrote:
| From 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 wrote:
| I 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.
| gfunk911 wrote:
| Exactly. I find it helps to also state that it doesn't mean
| "Don't Repeat Characters"
| dllthomas wrote:
| I've pointed out in the past that if the characters you're
| deduplicating don't actually have the same meaning, you're
| just compressing your code; I've been trying to popularize
| labelling that kind of aggressive misapplication of DRY
| "Huffman coding".
| cogman10 wrote:
| It's because you are using the wrong term that it's not
| caught on. Obviously this is run length coding or maybe
| LZ coding ;).
| marcosdumay wrote:
| On the other hand, if you keep repeating the same
| character sequence there is probably some structure on
| your code that you can also abstract away. You are not
| restricted to factoring business concepts.
| dllthomas wrote:
| There's clearly structure you _can_ abstract away; doing
| so may be convenient, and that convenience may even make
| it a good idea. I contend, though, that if it does not
| represent a "piece of knowledge" that you're factoring
| out then you are dealing with concerns other than "DRY".
|
| I do agree that a piece of knowledge may not be a
| business concept.
| Jtsummers wrote:
| That's actually more or less how DRY is described in _The
| Pragmatic Programmer_ (20th anniversary edition):
|
| > Every piece of knowledge must have a single, unambiguous,
| authoritative representation within a system.
|
| Further on:
|
| > Dry is about the duplication of _knowledge_ , of _intent_.
| It 's about expressing the same thing in two different
| places, possibly in two totally different ways. [emphasis in
| original]
|
| And the next paragraph:
|
| > Here's the acid test: when some single facet of the code
| has to change, do you find yourself making that change in
| multiple places, and in multiple different formats? Do you
| have to change code and documentation, or a database schema
| and a structure that holds it, or...? If so, your code isn't
| DRY.
| AnimalMuppet wrote:
| Early in my career, a co-worker and I developed the habit,
| when a bug was fixed, of asking each other "Did you fix it
| _everywhere_? "
|
| But that's the problem right there. Why is there more than
| one place to fix it?
|
| (Now, sure, if you're using a function call wrong, you need
| to fix it everywhere you call that function, and it's fine
| that there are multiple such places. That's what functions
| are _for_. But if it 's something like how a value is
| calculated... why is there more than one place that
| calculates the value?)
| chrisweekly wrote:
| Related 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.
| vikingerik wrote:
| The opposite principle of DRY is WET. Write Everything
| Twice
| ryanbrunner wrote:
| I think the old "remove duplication the third time you
| write it" is bad advice as well, to be honest.
|
| First off, it excuses some really egregious behaviour
| like copying entire large chunks of code representing an
| identical concept just because "I haven't copied it twice
| yet". I've seen an entire controller and all related
| views copied wholesale because it needed to be placed in
| another area of the app (with identical visual and
| functional requirements)
|
| Secondly, if the code is only incidentally identical,
| even 3 times isn't enough to say that you should apply
| DRY. 3 independent concepts that happen to currently
| share implementation still shouldn't be deduplicated. I
| think a great example of this is the inherited resources
| gem in rails that implements a "standard" interpretation
| of controller actions - which works well at first until
| your controllers need to do something other than basic
| CRUD. The fact that say, an update action handles
| parameters and saving in the same way as some other
| update action may currently be true, but is not at all
| guaranteed to be true in the future, even if you have 100
| update actions in your app.
| isleyaardvark wrote:
| > I think the old "remove duplication the third time you
| write it" is bad advice as well, to be honest.
|
| Same. "That's just DRY with extra steps!"
| alisonkisk wrote:
| marcosdumay wrote:
| If you know what application you are writing, there is
| absolutely no reason to do WET. You should always know if
| some code repetition is real or accidental.
|
| But we don't always know what application we are writing.
| BurningFrog wrote:
| My slightly different heuristic:
|
| If fact X changes, how many places in the code do we have to
| change it?
|
| If the number is > 1, relying on humans to realize that will,
| on average, always fail. I know of two ways to fix that.
|
| [1]: DRY it up.
|
| [2]: Write a test asserting that all the places use the same
| value.
|
| Sadly, most people rely on this:
|
| [3]: We "just have to remember" to do these changes in all
| places, despite the fact that human memory is unreliable, and
| the future person working on this code isn't even in the room
| when we decided this.
|
| Any time I hear a "we just have to remember" variation, either
| from coworkers or in my thoughts, an alarm goes off.
| recursivedoubts wrote:
| like 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/
|
| pic related:
|
| https://pbs.twimg.com/media/FAInxTuVkAATRrn?format=jpg&name=...
| darkwater wrote:
| Why 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 wrote:
| i 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
| dahart wrote:
| There 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.
| edflsafoiewq wrote:
| > 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 wrote:
| I'm about 80% sure that this graph eventually ends up back
| at the initial state.
| MauranKilom wrote:
| The axis is labelled "relative importance". If you place non-
| zero value on readability, you must place less than 100%
| importance on "does it work".
| manuel_w wrote:
| I 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 wrote:
| I'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
___________________________________________________________________
(page generated 2022-01-12 23:02 UTC)