Example: So much early code is boilerplate CRUD, that it's tempting to abstract it. 9 times out of 10, you'll create a quasi-ORM that starts inheriting business logic and quickly grows omni-functions.
Eventually you may actually need this layer, assuming you're system miraculously scales to needing multiple services, datastores, and regions.
However this doesn't just apply to the obvious, and you may find omni-logic that made a feature more simple once and is currently blocking N new features.
Code is cheap, especially today. Complexity necessarily constrains, for better or worse.
If I need to introduce the same feature in multiple places in roughly the same way, that's a decent indication code wants to be the same and wants to change together. That's something to consider extracting.
Fixing the same bug in several places is a similar, but weaker indication. It's weaker, because a bug might also occur from using a framework or a library wrong and you do that in several places. Fixing the same business logic error in several places could mean to centralize some things.
Is this "the same" thing? If so - extract and reference. Or is it "a different" thing which is superficially similar? Then don't.
Knowing when two things are one thing or one thing is two things is most of our job, right?
I think a better way to look at it than “third time - consider refactor” is to follow this article and ask “will this ever need to be extended?”. If the answer is yes, then you should duplicate it.
This way you won’t get a flying dog in your OOP hellscape but you also won’t have to change your holiday service 9 million places when your shitty government decides to remove one of them (thanks Denmark). Between the two, I would personally prefer working on the one where I have to do the 9 million changes, but I would obviously prefer neither.
Yes, but often we don't know the domain enough but "this feature must be available yesterday". So add tests, copy, release. And when you have to either do it again or have to update this code and its original you should know more and be able to refactor and give good names to everything.
A rule I like to follow:
- first time: write it
- second time: copy it
- third time: maybe refactor it