5 ms·
The Law of Leaky Abstractions (2002)
- Bostonian 3y agoShould have (2002) in title.
- deleted 3y ago[deleted]
- simonw 3y agoI love this essay so much. I read it 22 years ago and it's been stuck in my mind ever since: it taught me that any time you take on a new abstraction that you don't understand, you're effectively taking on mental debt that is likely to come due at some point in the future. This has made me quite a bit more cautious about the abstractions I take on: I don't have to understand them fully when I start using them, but I do need to feel moderately confident that I could understand them in depth if I needed to. And now I'm working with LLMs, the most opaque abstraction of them all!
- Legend2440 3y ago>And now I'm working with LLMs, the most opaque abstraction of them all! You put a black box around it to fit it into the world of abstractions that traditional programs live in. But I'd say the most interesting thing about neural networks is that they do not have any abstractions within them. They're programs, but programs created by an optimization algorithm just turning knobs to minimize the loss. This creates very different kinds of programs - large, data-driven programs that can integrate huge amounts of information into their construction. It's a whole new domain with very different properties than traditional software built out of stacked abstractions.
- avgcorrection 3y ago> Back to TCP. Earlier for the sake of simplicity I told a little fib, and some of you have steam coming out of your ears by now because this fib is driving you crazy. I said that TCP guarantees that your message will arrive. It doesn’t, actually. If your pet snake has chewed through the network cable leading to your computer, and no IP packets can get through, then TCP can’t do anything about it and your message doesn’t arrive. The argument is disqualified at this point. The whole world is a leaky abstraction because <freak meteor hit could happen>. At this point your concept is all-encompassing and in turn useless. There are assumptions: this computation will finish eventually [assuming that no one unplugs the computer itself]. This does not make things leaky. There are leaky abstractions I guess but not all are. A garbage collector that can cause memory errors would be leaky. I don’t know anything about garbage colletors but in my experience they don’t. Then someone says that a garbage collector is leaky because of performance concerns (throughput or latency). That’s not a leak: that’s part of the abstracting away part—some concerns are abstracted away. To abstract away means to make it something that you can’t fudge or change. To say that “this is implementation-defined”. An abstract list is an abstraction in the sense that it has some behavior. And also in the sense that it doesn’t say how those behaviors are implemented. That’s both a freedom and a lurking problem (sometimes). Big reallocation because of amortized push? Well you abstracted that away so can you complain about it? Maybe your next step is to move beyond the abstraction and into the more concrete. What are abstractions without something to abstract away? They are impossible. You have to have the freedom to leave some things blank. So what Spolsky is effectively saying is that abstractions are abstractions. That looks more like a rhetorical device than a new argument. (Taxes are theft?) EDIT: Flagged for an opinion? Very well.
- deleted 3y ago[deleted]
- mjw_byrne 3y agoI tend to agree. "All nontrivial abstractions are leaky" reminds me of other slightly-too-cute rules, such as "full rewrites are a mistake" and "never parse JSON manually". I wouldn't call TCP leaky because it can't deliver data across a broken network cable, for example. It's abstracting away certain unreliable features of the network, like out of order delivery of packets. It's not abstracting away the fact that networking requires a network.
- bxparks 3y agoI unflagged you by vouching for you. I found your post difficult to understand and couldn't figure out what you are trying to say, but I agree it was not deserving of a flag.
- avgcorrection 3y agoWhat was difficult about what I wrote?
- bxparks 3y agoYour criticism that a broken wire is outside the scope of the TCP protocol was clear and valid. The subsequent paragraphs about garage collection are tough to follow. You have multiple parenthetical remarks, a quotation which I think is used as emphasis, italics used as emphasis, compound sentence fragments joined by an em-dash and a colon in the same sentence, rhetorical questions which presumably have obvious answers but not obvious to me, terms that aren't clear (e.g. what is an "amortized push"?), and concepts that don't seem to be related to GC (e.g. an "abstract list" can be implemented without GC, so why is that included in that paragraph?). I've read those paragraphs 4-5 times now, and I don't think I understand what you are trying to say.
- bombela 3y ago
- evanmoran 3y agoIf you like this, my other favorite essay by Joel is Making Wrong Code Look Wrong: https://www.joelonsoftware.com/2005/05/11/making-wrong-code-look-wrong/ https://www.joelonsoftware.com/2005/05/11/making-wrong-code-...
- o11c 3y agoThe problem is that it completely ignores the correct solution, which is "use types; we invented them for a reason". HTML fragments should never be stored in strings.
- mason55 3y agoOne problem is that there's a major lack of language support to make this easy. IMO, every ID should be its own type. You shouldn't have a bunch of objects that have an ID of type string, you should have a User object with an ID of type UserID and a Post object with ID of type PostID, and then the compiler solves a lot of problems for you. Or make it so your functions that interact with the outside world accept a String, but they only return ValidatedStrings, and your internals only accept ValidatedStrings (and there's only one way to turn a String into a ValidatedString). But in any kind of language with structural typing (e.g. TypeScript) this doesn't work, by definition. You can call a string a UserID and you can call it a PostID but if they're both Strings then you can assign them to each other. And in Java, the concept of a typedef-like operation doesn't exist at all (I can't speak for .Net). There's a whole class of bugs that go away if you allow for easy, nominal typedefs, but it's actually not easy to do in most statically typed languages.
- mjw1007 3y agoI never liked the way he used TCP as an example here. I don't think it's sensible to think of "make it reliable" as a process of abstraction or simplification (it's obviously not possible to build a reliable connection on top of IP if by "reliable" you mean "will never fail"). "You might have to cope with a TCP connection failing" doesn't seem to be the same sort of thing as his other examples of leaky abstractions. TCP's abstraction is more like "I'll either give you a reliable connection or a clean error". And that one certainly does leak. He could have talked about how the checksum might fail to be sufficient, or how sometimes you have to care about packet boundaries, or how sometimes it might run incredibly slowly without actually failing.
- joe_the_user 3y agoIndeed, his discussion seems to involve a confusing of a leaky network protocol and a leaky abstraction. Perhaps he wanted to meta-illustrate his concept by having his discussion itself be leaky.
- lcuff 3y agoI like the idea of TCP as a leaky abstraction because it points out the difficulty of engineering the abstraction we really want. It would be wonderful for TCP to be a guaranteed connection abstraction, but it turns out in today's world, the abstraction of a reliable connection is TCP + a network administrator + a guy with wire snips + solder (metaphorically). Maybe down the road, AIs and repair bots will be involved, and the guaranteed connection abstraction might become real or much much stronger. Although it gets more complicated because if a message takes hours to deliver, is that going to work for your application? Yes if you're archiving documents, no if you're trying to set up a video conference call or display a web page. TCP is problematic in modern circumstances (think: Inside a data center) because a response within milliseconds is what's expected to make the process viable. TCP was designed to accommodate some element of the path being a 300 Baud modem, where a response time in seconds is possible as the modem dials the next hop, so the TCP timeouts are unuseable. QUIC was developed to address this kind of problem. My point being, the abstraction of a guaranteed _timely_ connection is even harder. I think Joel could have expanded his thoughts to include the degree of leak. SQL is a leaky abstraction itself, yes, but my own take is that ORMs are much leakier: Every ORM introduction document I've read explains the notation by saying "here's the sql that is produced". I think of ORMs as not a bucket with holes, but a bucket with half the bottom removed.
- BoiledCabbage 3y agoYoung people should probably know that (as far as I recall) Joel more or less invented tech blogging as a form of advertising/recruiting for your company. Namely either listing out the process/perks that a good engineering team should have and how conveniently his company has it. Or describing interesting and challenging problems they solved and how you can join them and solve problems like that too. I don't recall anyone popular doing it before him and it's pretty much industry standard now. (Although, feel free to chime in if that's wrong. But popular being a key word here),
- deleted 3y ago[deleted]
- williamcotton 3y agoCannot everyone get the sense that how we currently build software is one gigantic leaky abstraction?
- Legend2440 3y agoWorse; it's a stack of abstractions on top of abstractions on top of abstractions. You're at least 10 layers away from the hardware, possibly more.
- williamcotton 3y agoI don’t necessarily see that as the problem. Assembly is a great abstraction over machine code. Languages that compile to these bytecodes are a good abstraction. Garbage collected languages are a good abstraction. Web applications are not a good abstraction. Auth, storage, route handlers, tests, deployment, et al, are all cobbled together like chocolate ice cream and jalapeños on an uncooked bed of salmon and root beer.
- Legend2440 3y agoIt's not necessarily a problem. It would be very hard to program without abstractions. But all abstraction is compression in a sense. You are writing a smaller program that is "decompressed" into a much larger compute graph - for example, a loop unrolls into the same operation many times. This makes it much easier to fit the program into your head, but also limits its complexity. This is why it's hard for programs to deal with the arbitrarily-complex real world.
- williamcotton 3y agoArbitrarily complex domains are one thing. Arbitrarily complex web applications are another.
- deleted 3y ago[deleted]
- 3y ago
- highfrequency 3y ago> “All abstractions leak, and the only way to deal with the leaks competently is to learn about how the abstractions work and what they are abstracting. So the abstractions save us time working, but they don’t save us time learning.” Very nicely worded. But I would also add that: 1. An abstraction can often be manned by one person, so when it leaks only one person needs to understand it deeply enough to fix it. 2. The article seems to miss the iterative nature of abstractions. Over time, the goal is to iterate on the abstraction so that it exposes more of the stuff that matters, and less of the stuff that doesn’t matter. Perhaps all abstractions leak, but some leak way less often and save much more thinking in the meantime than others. Rather than lamenting the nature of abstractions we should focus effort on making them as practically useful as possible.
- glial 3y agoThis is well-written. I might suggest that what makes pure mathematics special is that abstractions in pure math are not leaky, unlike in (nearly?) every other domain.
- mturmon 3y agoDidn't downvote, don't entirely disagree, but maybe it would be OK to say that the leaks can be made more apparent: "The integral reverses the derivative" † ‡ * † Up to an arbitrary additive constant ‡ Provided the derivative exists * And we hope you don't have concerns about the existence of the real numbers
- glial 3y agoTrue; I was thinking about abstract algebra, set theory, and number theory when I wrote that.
- an1sotropy 3y agoI first learned about "leaky abstractions" from John Cook, who describes* IEEE 754 floats as a leaky abstraction of the reals. I think this is a good way of appreciating floating point for the large group of people who's experience is somewhere between numerical computing experts (who look at every arithmetic operation through the lens of numerical precision) and total beginners (who haven't yet recognized that there can't be a one-to-one correspondence between a point on the real number line and a "float"). * https://www.johndcook.com/blog/2009/04/06/numbers-are-a-leaky-abstraction/ https://www.johndcook.com/blog/2009/04/06/numbers-are-a-leak...
- cloogshicer 3y agoI've long been having a hunch that we're currently in the "wild west of abstraction". I think we're missing an essential constraint on the way we do abstraction. My hunch is that this constraint should be that abstractions must be reversible. Here's an example: When you use a compiler, you can work at a higher layer of abstraction (the higher-level language). But, this means you're now locked into that layer of abstraction. By that I mean, you can no longer work at the lower layer (assembly), even if you wanted to. You could in theory of course modify the compiler output after it's been generated, but then you'd have to somehow manually keep that work in sync whenever you want to re-generate. Using an abstraction kinda locks you into that layer. I see this problem appearing everywhere: - Use framework <--> Write from scratch - Use an ORM <--> Write raw SQL - Garbage collection <--> Manual memory management - Using a DSL <--> Writing raw language code - Cross platform UI framework <--> Native UI code - ... I think we're missing a fundamental primitive of abstraction that allows us to work on each layer of abstraction without being locked in. If you have any thoughts at all on this, please share them here!
- ihumanable 3y agoLots of abstractions have an escape hatch down to the lower level, you can put assembly in your C code, most ORMs have some way to just run a query, etc. I think the question I have is, what benefit does this provide? Let's say we could wave a magic wand and you can operate at any layer of abstraction. Is this beneficial in some way? The article is about leaky abstractions and states > One reason the law of leaky abstractions is problematic is that it means that abstractions do not really simplify our lives as much as they were meant to. I think I'm just struggling to understand how this would help with that.
- cloogshicer 3y agoIt would help because you could tackle the problem at hand always at the right layer of abstraction. If a certain aspect of the problem can be solved easily in a higher layer of abstraction, great! Let's solve it at that layer, because it's usually easier and allows for more expressiveness. But whenever we need more control, we can seamlessly drop down to the lower layer and work there. I think we need to find a fundamental principle that allows this. But I see barely anyone working on this - instead we keep trying to find higher and higher layers of abstractions (LLMs being the most recent addition) in the hopes they will get rid of the need of dealing with the lower layers. Which is a false hope, I feel.
- titzer 3y agoJoel of course hits the nail on the head about the two major things that cause abstractions to fall apart: performance and bugs (or debugging). In programming languages we talk about abstractions all the time--PL of course is all about abstractions. A computational abstraction like a bytecode, source language, or even machine code, can be proven be a proper (or full) abstraction, meaning there is no way for implementation details to leak in--you cannot observe the electrons flowing by executing A+B, after all. ...until you start measuring sidechannels, or the CPU or compiler has a bug. I think about this a lot when dealing with VMs; a complex VM cannot hide its complexity when programs care about execution time, or when the VM actually has a bug.
- refactor_master 3y agoA car is an implementation meant to deal with a problem (the weather), but never abstracts away physics or forces full buy-in to some alternate reality. You can’t just go around and say any imperfection in an implementation is a leaky abstraction. That’s not how it works. My shoe is not abstracting away the terrain, nor is it leaky because it doesn’t handle all weather conditions. Well, it is leaky, but not in that sense.
- wvenable 3y agoI loved this essay when it came out but I've come to dislike how "leaky abstraction" has become a form of low effort criticism that gets applied to almost anything.
- davesque 3y agoI feel like this article should be called "The Law of Bad Abstractions." I often see this cited as a blanket rejection of complexity in software. But complexity is unavoidable and even necessary. A skillful engineer will therefore design their abstractions carefully and correctly, balancing time spent thinking forward against time spent implementing a solution. I think Joel understands this, but it feels weird how he frames it as a "law", as though it's something he's discovered instead of a simple fact that arises from the nature of what abstractions are: things that stand in for (or mediate interaction with) some other thing without actually being that thing. What a surprise that the stand-in ends up not being the actual thing it's standing in for!
- tgma 3y agoBuilding White-Box Abstractions by Program Refinement[1] [1]: https://people.inf.ethz.ch/suz/publications/onward16.pdf https://people.inf.ethz.ch/suz/publications/onward16.pdf
- lmm 3y agoThis is wrong and even just looking at the examples is enough to understand that it's wrong. Writing your program to use UDP instead of TCP won't make it work any better when someone unplugs the network cable. An abstraction performing worse isn't a "leak" - the abstraction is still doing what it said it would (e.g. the SQL query still returns the right results), and in practice very few query planners are worth tuning manually (indeed PostgreSQL doesn't even offer you the ability to do hints etc., and that doesn't seem to hurt its popularity). I've never understood why this post was so popular - it only ever seems to be used as an excuse for those who want to write bad code to do so.
- falserum 3y ago> Writing your program to use UDP instead of TCP won't make it … There was no proposal to use UDP, so this comment is not about the article. The point of the article is near the end: > the only way to deal with the leaks competently is to learn about how the abstractions work and what they are abstracting. So the abstractions save us time working, but they don’t save us time learning. I.e. To competently use an abstraction, one needs to understand what happens under the hood.
- rancour 3y ago[dead]
- jacobmarble 3y ago> So the abstractions save us time working, but they don’t save us time learning. I could say the same for programming with Copilot.
- foobarian 3y ago> But sometimes the abstraction leaks and causes horrible performance and you have to break out the query plan analyzer and study what it did wrong, and figure out how to make your query run faster Hah. The more things change, the more they stay the same.
- nextworddev 3y agoThis is a long winded way to say that every abstraction has failure modes
- falserum 3y agoThats the first part. The second part is that you must acknowledge that it is JUST an abstraction, and learn&understand what actually happens.
- xtiansimon 3y agoAre "models" and "abstractions" the same thing? A recent post, `Mental Models: 349 Models Explained...` reminded me of the `Debits and Credits Model`, which works because of the Debit and Credits Formula or Accounting Equation (Assets = Equity + Liabilities). Minor leaks happening here and are usually stuffed into an account--so we don't have to eat lunch at our desks. The abstraction examples seem similar, but the discussion around leakage is interestingly different. For example @anonymous-panda suggests you sometimes want your abstraction to be leaky: _ "...leaky abstraction would be when you need to still distinguish the error type and TCP wouldn’t let you..."_