An abstraction is useful when it hides the right things.
Good abstractions:
- TCP/IP hides physical medium; you think in packets
- SQL hides storage layout; you think in sets
- Functions hide implementation; you think in transformations
Bad abstractions hide the wrong things — they leak. When a leaky abstraction breaks, you have to understand both layers at once, which is harder than understanding either alone.