Leaky Abstraction
About this...
An abstraction that leaks details that it is supposed to abstract away
🌱 This wiki entry hasn't fully bloomed. It's very likely to change over the next little while.
ORMs like Sequelize and Prisma use the Active Record pattern which lets you add, remove, delete, and update data from SQL server without ever needing to write any SQL code. For simple cases, they work amazingly. They even tend to work pretty well for more non-trivial queries as well. The downside is that when we run into the scenario where we need to use our ORMs to write really complex queries, it's not uncommon that they plan and execute inefficient SQL code. In situations like this, the abstraction of the ORM has leaked, and we're forced to either learn how to make the ORM write a more performant query, or to manually write the more efficient SQL query ourselves.
The Law of Leaky Abstractions
Joel Spolsky, co-founder of StackOverflow, famously coined the Law of Leaky Abstractions which states:
All non-trivial abstractions, to some degree, are leaky.
Much of software development is creating abstractions. We write collections to abstract a list of items. We write value objects to make objects appear as if they're primitive types. We use the file system API which makes it seem like it's a structured hierarchy of files and folders. We write RESTful APIs and client libraries to abstract the complexity of interacting with our backend services.
All non-trivial abstractions (the more complex ones), to some degree, leak. As in, they leak implementation details.
Examples in modern tooling
-
React.js — React is a view layer library that lets you create user interfaces. While the API for React is small and you can get far by following the rules of React (like only put hooks in classes with render functions or other hooks), it's not uncommon to run into scenarios where you need to learn more about how React determines when to re-render, when it calls effects and when components are unmounted.
- Apollo Client — Apollo Client automatically handles the GraphQL data fetching logic and caching logic for you. However, to evict or manipulate data in the cache, you need to learn how it normalizes that cached data.
- The Django Web Framework — I swear this is 95% magic.
You can probably think of thousands more leaky abstractions. If it's non-trivial (meaning, it does something really substantial), then it's only a matter of time until you run into a leak.
Should we not use tools with leaky abstractions?
No, that's not the point. Tooling ultimately saves us time. Who wants to go and write another React for the sake of... oh wait.
Alright, well. The point is to remember The Law of Leaky Abstractions. Keep in mind that everything non-trivial will eventually leak. And when it does, you may have to roll up your sleeves to understand how the abstraction works behind the scenes.
This can help you make decisions as to what kinds of tools you want to use.
Don't believe that anyone that says their tool can abstract everything away. It's just a matter of time until you're forced to learn the internals.
How do we apply this knowledge to our coding practices?
When you're designing abstractions, you want to make sure that other developers understand how to use them strictly from the public interface. You want to shield others from the complexity behind the scenes of your abstraction.
But if something doesn't work correctly, or they can't figure out how to fit what you wrote for their use case, it's said to be a leaky abstraction.
See here for an example.
Bonus (aside): Mark Phillips (Supreme Dreams) has a really funny video that almost perfectly demonstrates the idea of a leaky abstraction in real life. Check it out.
Related
- Maintain a Single Layer of Abstraction at a Time | Object-Oriented Design Principles w/ TypeScript
- Coupling, Cohesion & Connascence | Wiki
Also read
Join 20000+ value-creating Software Essentialists getting actionable advice on how to master what matters each week. 🖖