An abstraction is a promise that you don’t have to think about what’s underneath. The filesystem promises you files and directories so you can forget about sectors and platters; the network socket promises a stream of bytes so you can forget about packets and retransmissions. These promises are enormously productive — the whole edifice of software is layers of them stacked on layers, each letting you work without holding the one below it in your head. Nobody could build anything if every task required understanding all the way down. The abstraction is what makes the height possible.

But the promise is never total, and the gap is where the trouble lives. The filesystem is fast until the disk is remote and a read that used to take microseconds takes half a second. The byte stream is reliable until the connection drops mid-message and you discover that “stream of bytes” was always a story told on top of packets that can be lost. The abstraction held right up until some property of the underlying layer — latency, failure, ordering, cost — pushed through the interface and became your problem again. This is the sense in which all non-trivial abstractions leak: they hide the layer below until a characteristic of that layer refuses to stay hidden.

What follows from this isn’t that abstractions are bad or that you should always work at the lowest level — that way lies never finishing anything. It’s that the layer you’re standing on will eventually demand that you understand the one beneath it, and the only question is whether that happens at a moment of your choosing or in the middle of an incident. The engineers who look like they have magic are usually just the ones who, when the abstraction leaked, already knew enough about the layer below to recognize what they were seeing. They didn’t avoid the leak; they were ready for it, because they’d spent some of their attention downward before they were forced to.

This suggests a way to judge abstractions that has nothing to do with how much they hide. The dangerous ones hide the underlying layer so completely that when it does break through, you have no framework for what’s happening — the leak arrives as an inexplicable anomaly, a slow query or a phantom failure with no visible cause, because the abstraction spent all its effort pretending the lower layer didn’t exist. The good ones hide the layer for convenience but don’t lie about its existence: they surface a timeout when the network is slow, an explicit error when the disk is full, a signal that says “the thing I was hiding is now relevant.” A leak you were warned about is a manageable event. A leak from an abstraction that promised there was nothing underneath is a mystery, and mysteries are expensive.

So the craft isn’t building abstractions that never leak — that’s not on offer. It’s building ones that leak honestly, and using ones that do. Know roughly what’s below the layer you’re working on, not in full detail but enough to recognize its fingerprints when they appear. Prefer the abstraction that fails loudly over the one that fails silently, even when the silent one feels cleaner on the good days. The good days aren’t when abstractions earn their keep — every abstraction looks perfect when nothing underneath is going wrong. They earn it on the bad day, by the way they behave when the layer they were hiding decides to make itself known.