4 ms·
Exactly. The complexity is inherent in the domain, and making it seem simpler by sweeping it under the carpet is not solving the problem, and it will result in
by bjz_ 11y ago
Exactly. The complexity is inherent in the domain, and making it seem simpler by sweeping it under the carpet is not solving the problem, and it will result in hard to find bugs when working at scale.
- dllthomas 11y agoThere's abstraction, leaky abstraction, and brokenness. Making things seem simpler by finding an abstraction over them is great! Making things seem simpler by finding an abstraction that leaks some details so you sometimes need to handle them at a high level is... less great. But sometimes still a win. Making things seem simpler but neither handling the complexity nor allowing your users to handle the complexity is technical debt at best.
- catnaroek 11y agoMaybe this is just a sign of my lack of experience, but I don't think implementing leaky abstractions is ever justified outside of systems programming. (Implementing a runtime system for a manged language counts as “systems programming” in my book.) Here are some examples of justified leaky abstractions: (0) Memory is infinite, and memory allocation will always succeed. If it doesn't, aborting the entire process is the right course of action. (1) Communication over a network is reliable and data will arrive in the same order in which it was sent. And here are some examples of unacceptably leaky abstractions: (0) A few basic data structures [in particular, growable arrays and hashtables] suffice to implement every algorithm you might ever want to implement. (1) Strings are arrays of characters, and thus you can efficiently index them by character. (2) Every [nonempty] string is a valid file name or URL. (3) Every external resource can be manipulated using an API that presents it as a sequence of bytes. (4) An event loop is the universal solution to all asynchronous I/O problems.
- dllthomas 11y agoI don't see how your examples line with "systems programming" vs "not systems programming". I think in general, building on top of leaky abstractions is a form of technical debt, and you have to be aware of how much you're accumulating and what you're getting in exchange. Also, complexities stack super-linearly. I'm tempted to say, additionally, that building shallowly atop a leaky abstraction is less problematic than pushing leaks further down in a tall stack... but on reflection I have no idea how I'd support that.
- catnaroek 11y agoBuilding on top of leaky abstractions isn't necessarily a form of technical debt. Some leaky abstractions are actually better than their non-leaky counterparts. For example, not worrying about the possibility of running out of memory lets you concentrate on whatever your program is actually supposed to do, rather than micromanaging a very improbable situation. So, what I meant to say in my previous post is that, as far as I can tell, good leaky abstractions only arise when doing systems programming, or, more precisely, at the boundary between low-level system services (e.g. runtime systems for managed languages) and the applications that consume them. Abstraction leaks in high-level application code are likely to be bad.
- dllthomas 11y agoHmm. I think that's a little confused. You need to worry about running out of memory precisely because the abstraction is leaky. If you need to micromanage, it's perhaps not leaky, but it's also not much of an abstraction. Exploring this example a little deeper, if our model is "we never run out of memory, but our program may stochastically die at any point in time", then that's not as leaky as "we never run out of memory", and our program may die for reasons outside our control so it's a good idea to be robust against those. But now let's say we need to send a request to a service, and the service disconnects when we've sent the request. With a stochastic model of failures, we clearly should just re-send. But if the problem was that our payload demanded more memory than could be provided, retrying repeatedly will just keep killing servers. We probably want a model of "we never run out of memory, our program may die at any point in time, possibly correlated with inputs", which I would contend is actually not very leaky. We still spill over into performance, but most things spill over into performance... I would say that leaky abstractions constitute debt relative to non-leaky equivalently powerful abstractions. They may still pay their way.
- catnaroek 11y ago> You need to worry about running out of memory precisely because the abstraction is leaky. In practice, I don't worry. I'm fine with the leaky abstraction, because the benefit of fixing the leak (reducing the probability that OOM crashes my program from almost zero to exactly zero) isn't worth the cost (increased complexity). But see below. > If you need to micromanage, it's perhaps not leaky, but it's also not much of an abstraction. I'm not sure about this. There are levels of abstraction and degrees of micromanagement. For instance, `malloc` lets you micromanage the memory layout of your data structures, and `mmap` and `mprotect` also let you micromanage what you can do with each memory page, but they're still abstractions over even lower-level concerns, like the mapping between physical and virtual memory addresses. > We probably want a model of "we never run out of memory, our program may die at any point in time, possibly correlated with inputs", which I would contend is actually not very leaky. Depends on what kind of program you're writing. If your web crawler crashes, you just restart it. If your web server crashes, your users will be annoyed, but at least your database won't be compromised. (You're properly using transactions, right?) But if your DBMS crashes, your data might even be in an irrecoverably inconsistent state, unless you properly anticipated all (software) causes for it to crash.