Hacker News new | past | comments | ask | show | jobs | submit login

I don't even know how to comment to something like this. I guess with an example - I spent some weekend time fixing 30 file paths in a Cloud function, because the junior developer couldn't abstract the root to a single method.

After you do something like that, tell me how much you love fixing bugs everywhere.




Maybe you misunderstood; I am not saying avoid DRY. I am responding to a thread wherein the parent said, essentially, "confirm that it's really repeating".

My whole point was that we reach for DRY too early, when we don't know if it's actually repeating ourselves. The logic -right now- looks the same. Will it be in the future? Most of the time, we don't. Even when we think it will, we're often wrong. Your example sounds like there's literally a string literal that was not abstracted out, but hardcoded 30 places. That isn't logic. It's a literal. It has one meaning, and you can probably ascertain whether or not the meaning is the same across everything. And, as mentioned, it seems weird you couldn't grep for it.

Either way though, as a counter example - I and a coworker spent three months playing whack a mole extending and supporting a rather small desktop application written by one, extremely senior developer, who had assumed 7-8 different things were basically the same, and so had written it to share a lot of the same code. They were not the same, and so a fix for one thing invariably broke two others. I was thinking of it in particular with my original post; we fixed one thing, and two more things would be broken.

I got permission to rewrite the application over the course of a couple of days from the team lead, and proceeded to basically do a lot of copy paste to separate out each thing into its own control flow, where a fix to one would not affect the others. Our MTTF plunged, and within a month it was basically stable.


Sorry about misunderstanding.

I think that's where SRP and naming comes in like mentioned elsewhere in the thread. If methods really do one simple thing and are properly named, it's easy to tell if they should be re-used or not.

A lot of the time, side-effects are not mentioned in method names or params (or the params are optional, that's where the issue comes in).

E.g.

SaveCandidate(Candidate candidate);

In reality, SaveCandidate does two things, saves the candidate and updates their "ProfileCompletionStatus".

If someone sees SaveCandidate and not SaveCandidateAndUpdateCompletionStatus

or

SaveCandidate(candidate, SaveSideEffects.DO_UPDATE_COMPLETION_STATUS)

they will re-use SaveCandidate in places here completion status shouldn't be updated (contrived example - data migrations).

The problem, of course, is that these overly-clear, overly-detailed methods are a pain to read. The common advice is "it's separate", but then we are repeating

SaveCandidate(candidate); UpdateProfileCompletionStatus(candidate);

everywhere, despite them being generally one process. I guess this is all pretty pedantic. When you spend a ton of time on this, you are missing out on getting stories done, but if you don't, the code eventually becomes umanageable.


Right, but then someone sees that, and goes "Ah-hah. This is saving Candidates. Over here I'm also saving Foos. I'll refactor this so I have a generic 'Save' method/function, and then both Candidate and Foo can call it. DRY!

And that works, because initially saving is just taking a serializable object and dumping it to JSON. But then things get moved to going to a DB or something, and now it has different expectations, and, oh, crap, we have to do some special logic in the transaction because it turns out saving a foo requires us holding a lock on the bar table as well, but not for candidates, and etc etc etc, and saving was -really just two lines of code in the first place-, and the correct solution is isolating it entirely (with maybe a Saver interface to say 'yeah, this thing knows how to save itself'. Though that likely breaks SRP since now Foos know how to be Foos, and how to save Foos, which involves knowledge of the DB, which seems suboptimal, but which is still cleaner than what you had before)

Etc. My only point is that if the area you're tempted to repeat is not provably the same, contextually, keep 'em separate until it is (even if it's like...they implement the same interface). The pain of getting that wrong is almost always less than combining them, building more assumptions on top of that, and then finding out they should have been separate.


Fixing the 30 file paths was not just a quick global search/replace?


Reminds me of the time I wrote 100 similiar sites separately with slight changes. A quick global search/replace will introduce so many unnecessary replaces unless you are careful and miss so many unless you take linebreaks into account.

A global search/replace is a hotfix hammer that should only get pulled out rarely and carefully by manually reviewing all changes.


Bad example, it was more complicated than that. Azure functions have a different file system than App Services, and the method tried to determine where it was hosted 30 times, or something like that. Genuinely don't remember.


Refactoring repeated code is toil, but cognitively trivial. Refactoring a bad or leaky abstraction can be fiendishly difficult. I'll take the toil any day.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: