I feel like every coder has to overdo it at least once to truly grok why it is a balancing act. And I think that oftentimes leads to the discovery that every best practice is a balancing act.
A good rule is two maybe even three duplications is ok. After that, probably tome to refactor at least a little. Often this rule of thumb saves some efforts.
The rule should not be primarily based on a number of duplications but if we know that all the duplicated parts need to be changed when one is changed.
You're probably right on that. I now weigh my options to try to find a middle ground between impossible to change because it wasn't designed for change and impossible to change because it was designed for too many future use cases that will never happen.
IME, extensible code comes from thinking of possible extensions and then not getting in their way, which usually just means keeping it simple. Easy to say but hard to do.