top | item 42531370

(no title)

rauljara | 1 year ago

I wish articles like this had more examples in them. In between “this thin wrapper adds no value but a lot of complexity”, and “this thin wrapper clarified the interface and demonstrably saved loads of work last time requirements changed” is an awful lot of grey area and nuance.

I did like the advice that if you peak under the abstraction a lot, it’s probably a bad one, tho even this I feel could use some nuance. I think if you need to change things in lots of places that’s a sign of a bad abstraction. If there is some tricky bit of complexity with changing requirements, you might find yourself “peeking under the hood” a lot. How could it be otherwise? But if you find yourself only debugging the one piece of code that handles the trickiness, and building up an isolated test for that bit of code, well, that sounds like you built a wonderful abstraction despite it being peaked at quite a bit.

discuss

order

CalChris|1 year ago

The article did start off giving TCP as a good abstraction but then didn't follow up with examples of bad abstractions.

Dynamic typing is an example of an indirection masquerading as an abstraction. You end up carrying around an object and occasionally asking it whether it's an int64_t or a banana. You maybe think your type luggage will take you on exotic vacations when really in fact you take it on exotic vacations.

miki123211|1 year ago

To me, it ties in with John Ousterhout's concept of "deep, small interfaces"

TCP is a good abstraction because it's essentially 4 operations (connect, disconnect, send, receive), but there's a lot going on inside to make these operations work. So are TLS, filesystems, optimizing compilers and JITs, modern CPUs, React (or rather the concept of "reactive UI" in general), autograd and so on.

throwawaymaths|1 year ago

Isn't there a flip side to this? earlier today i saw someone tweet that monads are indirection, not abstraction.

mexicocitinluez|1 year ago

Amen.

Articles like this are a dime a dozen. Literally, there are 1000s of articles that all say the exact same thing using way too many words: "Bad abstractions are bad, good abstractions are good".

epolanski|1 year ago

I second this, such posts are very generic, they are hard to disagree with, but also to agree with empathically as there are no clear examples of what is too much.

As someone who uses lots of layers and dependency injection I would like to be poked on where is that too much abstraction but I end up being no wiser.

brunorb8|1 year ago

I believe that the place to find detailed examples and deep analysis is in books, not one-off web articles.

mkoubaa|1 year ago

The way to tell whether an abstraction is good or bad is to develop good taste. Engineers with good taste have intuition about these things.

You are not going to acquire good taste from reading an article.

layer8|1 year ago

Relying on mere “taste” is bad engineering. Engineers do need experience to make good decisions, yes. But surely we are able to come up with objective criteria of what makes a good abstraction vs. a bad abstraction. There will be trade-offs, as depending on context, some criteria will be more important than other (opposing) criteria. These are sometimes called “forces”. Experience is what leads an engineer in assessing and weighing the different present forces in the concrete situation.

baobabKoodaa|1 year ago

Maybe not, but you can still move the needle one way or another based on reading an article. For those readers who recognize themselves as erring on the side of adding too many abstractions, they might move the needle a bit towards the other side.

rmbyrro|1 year ago

try using LangChain and you'll get countless examples of bad abstractions

started working with it this week for a new project

gosh, it's so painful and unintuitive... I find myself digging deep into their code multiple times a day to understand how I'm supposed to use their interfaces

jerf|1 year ago

"I wish articles like this had more examples in them."

There is a class of things that don't fit in blogs very well, because any example that fits in a blog must be broken some other way to fit into a blog, and then you just get a whole bunch of comments about how the example isn't right because of this and that and the other thing.

It's also a problem because the utility of an abstraction depends on the context. Let me give an example. Let us suppose you have some bespoke appliance and you need to provide the ability for your customer to back things up off of it.

You can write a glorious backup framework capable of backing up multiple different kinds of things. It enforces validity checks, slots everything nicely into a .zip file, handles streaming out the backup so you don't have to generate everything on disk, has metadata for independent versions for all the components and the ability to declare how to "upgrade" old components (and maybe even downgrade them), support for independent testing of each component, and has every other bell and whistle you can think of. It's based on inheritance OO and so you subclass a template class to fill out the individual bit and it comes with a hierarchy pre-built for things like "execute this program and take the output as backup" and an entire branch for SQL stuff, and so on.

Is this a good abstraction?

To which the answer is, insufficient information.

If the appliance has two things to backup, like, a small SQL database and a few dozen kilobytes of some other files, such that the streaming is never useful because it never exceeds a couple of megabytes, this is an atrocious backup abstraction. If you have good reason to believe it's not likely to ever be much more than that, just write straight-line code that says what to do and does it. Jamming that into the aforementioned abstraction is a terrible thing, turning straight code into a maze of indirection and implicit resolution and a whole bunch of code that nobody is going to want to learn about or touch.

On the other hand, if you've got a dozen things to backup, and every few months another one is added, sometimes one is removed, you have meaningful version revs on the components, you're backing up a quantity of data that perhaps isn't practical to have entirely in memory or entirely on disk before shipping it out, if you're using all that capability... then it's a fantastic abstraction. Technically, it's still a lot of indirection and implicit resolution, but now, compared to "straight line" code that tries to do all of this in a hypothetical big pile of spaghetti, with redundancies, idiosyncracies of various implementations, etc., it's a huge net gain.

I don't know that there's a lot of abstractions in the world that are simply bad. Yeah, some, because not everything is good. But I think they are greatly outnumbered by places where people use rather powerful, massive abstractions meant to do dozens or hundreds of things, for two things. Or one thing. Or in the worst case, for no things at all, simply because it's "best practices" to put this particular abstraction in, or it came with the skeleton and was never removed, or something.