Knowing what and when to abstract can be hard to define precisely. Over abstraction has a cost. So does under abstraction. I have seen, wrote and refactored terrible examples of both, written by myself and others. Anecdotally, flattening an over abstracted hierarchy feels like less work and usually has better test coverage to validate correctness after refactoring than abstracting under avstracted code (spaghetti code, linear code, brand it how you will). Be aware of both extremes and try to find the balance.
Knowing what and when to abstract can be hard to define precisely. Over abstraction has a cost. So does under abstraction. I have seen, wrote and refactored terrible examples of both, written by myself and others. Anecdotally, flattening an over abstracted hierarchy feels like less work and usually has better test coverage to validate correctness after refactoring than abstracting under avstracted code (spaghetti code, linear code, brand it how you will). Be aware of both extremes and try to find the balance.