This website uses cookies

Read our Privacy policy and Terms of use for more information.

The process of abstraction can be elegant when done correctly. The concept is exciting: I don’t need to worry about the details. I can focus on the essentials, and the rest is taken care of. Unfortunately, in my experience, this is rare. Oftentimes, something gets in the way of such an elegant implementation. Design timelines may prevent me from finding a clean solution; functional or non-functional constraints may force an awkward interface; or I might make a mistake and fail to consider a scenario that causes a leaky abstraction.

Almost 24 years ago, Joel Spolsky wrote The Law of Leaky Abstractions. In covering an example of abstraction via IP and TCP, he declared this law:

All non-trivial abstractions, to some degree, are leaky.

Joel Spolsky

I agree with Joel, and it exposes something I have considered over the past few months. Abstraction is not about the indefinite guarantee of “never having to think about the intricate details,” it is about scoping down complexity for the time being. We can enjoy closed-world models and isolated simulations, “in theory” this and “technically” that, but the real world has entropy. This is why guarantees are littered with caveats. In the software world, we attach service-level agreements to our guarantees because we can only provide these guarantees within specific bounds. If anyone wants to know why, we have to pull out the architecture diagrams and show that the system is designed in a way that demands these constraints. Then we have to dive into why these constraints occurred. If we dig deep enough, we get into timelines, business decisions, initiatives; at the end of this chain is perhaps an executive who set an ambitious timeline because the meeting to decide on it was right before lunch.

Abstraction is a tool to delay the inevitable; a “limited warranty” rather than a “lifetime guarantee.” This is second nature when applied to learning. While the long-term objective is to dive all the way in and learn everything about a specific subject, our brains demand that we press on the brake pedal and focus on one thing at a time. If I set out to learn about the History of the United States, I can’t learn it all at once. I will likely focus on one era at a time, in chronological order. Consider studying the 1860s as an example. Abraham Lincoln was president from 1861 to 1865, so I would study his presidency. I am aware that he is on the $5 bill, but that did not happen until 1914. I am willing to accept my ignorance of the history of the $5 bill while studying the 1860s, because the details are not yet relevant. Keyword: yet.

At every abstraction I encounter, I can identify a hypothetical moment where I can no longer rationalize my ignorance. Eventually, I will need to pull back the curtain and see what is going on. Consider a manager of an engineering team. A manager can operate as a two-way abstraction. She takes top-down communication and distills it into relevant information for her engineers. She also takes project details from the engineers and distills them into a clean report for leadership. For the happy path, this works. However, the abstraction rapidly shatters once a project becomes unhealthy. From the engineer’s perspective, the top-down pressure starts to break through their manager and impacts daily operations. The engineer may work overtime, rush out lower-quality features, or even re-rank priorities and start cutting features to make the deadline. On the other side, leadership may demand some form of detailed audit. In both cases, the contract is altered, and the abstraction is cracked wide open. Layers once shielded from one another now have a higher degree of exposure.

By this logic, a “good” abstraction becomes one that shields the happy path and the most frequent failure scenarios. The abstraction will still have some sort of edge case that breaks it, but the goal is to ensure those scenarios are infrequent and more indicative of misuse of the interface than a fundamental problem with it. This is where SLAs come in. They give a clear boundary where the interface won’t break, and allow engineers to evaluate their systems to see if they will get all the guarantees that the system offers. Because software exists in the real world, SLAs usually also define expected availability to account for outages. Breaking this SLA is a big deal. Reduced reliability breaks trust, and trust ruins faith in abstraction. GitHub made headlines for downgrading to a single 9 of uptime by March of 2026. As a result, some large projects publicly migrated off GitHub.

A good abstraction is a shared responsibility. The interface owner must be open about its limitations and work diligently to maintain guarantees within the SLA. The interface consumer must understand the limitations and know when their usage requires breaking the abstraction to gain a deeper understanding of the underlying system.

Reply

Avatar

or to participate

Keep Reading