> In practice what I see fail most often is not premature optimization but premature abstraction
This matches my experience as well.
Someone here commented once that abstractions should be emergent, not speculative, and I loved that line so much I use it with my team all the time now when I see the craziness starting.
Some abstractions are obvious though. There is value in reusing them across projects and not reinventing the wheel each time. I don’t think this is counter to what you propose necessarily, just adding the nuance that abstractions serve another purpose: communicating intent to the readers of the code. Sometimes a concrete type is unnecessary detail.
> abstractions should be emergent, not speculative
Dang, I love that line, too. It is 100% correct, and the opposite of what OOP teaches.
I completely agree with you, and that is an amazing quote.
But they should emerge right when they are needed, not some time later via a painful refactor.
I think every dev lives in fear of the situation where abstraction hasn't been done when it really should have. Maybe some junior dev came in after you and shoehorned in a bunch of features to your "simple" solution. Now you have to live with that but, guess what, there's no time to refactor it, in fact the business just wants even more features.
As usual these rules work best if everyone on the team understands them in the same way. If you work with people who can only see what is right in front of them (ie. the current feature), then it'll never work. You can always fit "one more feature" into the perfect codebase without thinking about the abstractions.
“Abstractions should be written in blood”
Similar to when someone said that the rule of thumb should not be DRY (don't repeat yourself) but WET (write everything twice) - that is, be happy to repeat similar code once or twice, and wait for the need for abstraction to become evident.
It's advice I like, as I'm prone to falling into design paralysis while trying to think of the One True Abstract Entity.