Clarity is likely the most important aspect of making maintainable, extendable code. Of course, it’s easy to say that, it’s harder to explain what it looks like in practice.
I wrote a book that attempts to teach how to write clear code: https://elementsofcode.io
> 11. Abstractions don’t remove complexity. They move it to the day you’re on call.
This is true for bad abstractions.
> The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise. (Dijkstra)
If you think about abstraction in those terms, the utility becomes apparent. We abstract CPU instructions into programming languages so we can think about our problems in more precise terms, such as data structures and functions.
It is obviously useful to build abstractions to create even higher levels of precision on top of the language itself.
The problem isn’t abstraction, it is clarity of purpose. Too often we create complex behavioral models before actually understanding the behavior we are trying to model. It’s like a civil engineer trying to build a bridge in a warehouse without examining the terrain where it must be placed. When it doesn’t fit correctly, we don’t blame the concept of bridges.