Leaky Abstractions Aren’t Always Bad

A team goes out and installs a new lawn at a customer’s property. They’ve been there since 6am, it’s now 8pm, and they’ve finally got all the sod laid. The last step in the runbook says to program the sprinkler system. But the sprinkler system is broken, and nobody on the team can get it to work.

Now at this point everyone is exhausted and just wants to go home. One team member, Sam, who is very experienced and has done dozens of installs, is adamant. “We have to get the sprinklers running or the sod will die. That’s what the runbook says.”

But another team member, Alex, pushes back. “Look, the sod doesn’t need sprinklers. It needs water. Let’s just hose everything down with the spigot tonight and get the sprinkler company out here tomorrow to fix the system.”

Sam disagrees. The runbook says sprinklers. We need sprinklers.

Who’s right here?

Sam isn’t wrong, exactly. The sprinkler system does need to be fixed. But Sam is also stuck on the abstraction. The runbook says “program the sprinklers” because sprinklers are the normal mechanism for delivering water. But the actual requirement isn’t sprinklers. The actual requirement is that the sod needs to be watered. Alex understood what was happening underneath the abstraction and was able to devise a perfectly reasonable alternative that gets everyone home at a decent hour.

There’s a concept in software engineering called the Law of Leaky Abstractions, originally described by Joel Spolsky. The basic idea is that all abstractions, no matter how well designed, occasionally leak. The underlying implementation details bubble up through the abstraction layer, and if you don’t understand what’s going on underneath, the resulting errors and problems can seem almost mystical.

This is usually framed as a negative thing. TCP packets arriving out of order or across different network paths can cause bizarre intermittent behavior in your web application that you can’t observe or diagnose. A deployment pipeline fails in a way that makes no sense until you realize it’s a DNS caching issue three layers down. These are the moments where the abstraction leaks and you’re stuck.

But here’s what I think people miss. Leaky abstractions aren’t always a problem. Sometimes they’re an advantage. If you understand what’s actually happening behind the abstraction, you can exploit that knowledge to troubleshoot, work around problems, and find creative solutions that someone operating purely at the abstraction layer would never see.

This is what separates the engineer who opens the browser developer tools and immediately sees which backend call is returning the 500 error from the engineer who just keeps clicking the button and saying “it doesn’t work.” It’s the difference between someone who can base64 decode a SAML assertion to figure out why authentication is failing versus someone who just opens a ticket and waits. Both of those people are looking at the same problem. One of them can see through the abstraction to what’s actually happening underneath.

Going back to our lawn care example, Alex understood the leaky abstraction. The runbook was an abstraction over the real requirement, and when that abstraction broke down (the sprinklers didn’t work), Alex was able to reason about the underlying need and find another path forward. Sam, despite being more experienced, was locked into the abstraction and couldn’t see past it.

So the next time you or someone on your team hits a wall because the process or the tool or the system isn’t working the way it’s supposed to, take a step back and ask what’s actually going on underneath. The abstraction is leaking. That might be a problem, or it might be exactly the opportunity you need to find a better answer.

Leave a comment