Completely agree with you. The issue IMO is over-eager abstraction tends to simply make code more confusing. It may be more “elegant” but if it turns out the class/functions/whatever used to generalize code will never need to be used elsewhere (so they will only be changed in their current state), or have too many different behaviors based on configuration options, it actually makes things more confusing than just proliferating boilerplate - because instead of a big imperative chunk you can read and modify directly wherever it’s used, now you have to understand the class, maybe base classes, the function and which control paths it takes based on which args, etc.
And that nuance means for example, maybe it makes sense to create a helper to append some additional error detail to a string if used only twice, but maybe if some similar behavior is used in 7 slightly different ways, it shouldn’t be generalized if it requires lots of conditionals and arg plumbing.
This is why I kind of hate the mantra of “if it’s used 2-3 times or more, wrap it in a function or class.” IMO the bar for something being wrapped needs to be higher - either the amount of logic should be large and segmentable enough that it can be placed in something that separates concerns from where it otherwise would be, small and very repetitive, or it needs to be used much more often in a way that truly resembles a library (and thus requires an interface that does not expose implementation details or wrap messy conditional logic).