Traps and costs of failing abstractions
When talking about leaky abstractions, people often mention Joel Spolky's "Law of leaky abstractions":
All non-trivial abstractions, to some degree, are leaky.
Which usually turns into arguing semantics whether an abstraction can ever be truly "non-leaky", which have no practical meaning.
What have practical meaning, however, is the degree to which an abstraction is leaky and how well the abstractions is thought out. Bad abstractions can easily create much more complexity in the long run than they are able encapsulate. When referring to the leaky abstractions, I'm going to use the term in this broader sense.
Simple vs easy
Simpler abstractions (which doesn't hide as much complexity) are much easier to make less leaky. "Easier" abstractions (which try to hide the complexity) tends to be much more leaky and it's much harder to fully close the leaks.
See also: Simple vs easy
Costs of using abstractions
Before using any abstraction, it's crucial to understand the involved long term cost.
Bloat and performance costs
Make sure you really understand those - i.e. in the case of the web application, you/your users could be paying that price every and each time - on each request.
Try to move as much as you can of that price to the build time (that way you're going to pay it only once per build). Languages with good support for macros (i.e. Lisp) can help a lot with that.
Straying of their happy path
Anyone who have used RubyOnRails (or similar framework) long enough will know the nightmares of going "off the rails" once you have to implement something custom, which doesn't fit nicely into the happy path of the framework.
Debugging improper abstractions can be a nightmare (i.e. when we're talking about debugging kubernetes, there's no word to describe the horrors).