1. 25
  1. 14

    The distinction sounds to me a bit like No True Scotsman of software design. If it works for me, it’s an abstraction. If it doesn’t, it’s magic.

    1. 7

      Yes, I suspect it would be more constructive to catalogue properties of abstractions that are unhelpful. One mentioned briefly in the text is the “where is that [code] anyway” problem; i.e., when I’m trying to see through an abstraction because it isn’t working for me, how do I find the code that implements it?

      1. 7

        There is some subjectivity involved, and I don’t think this article does a stellar job at explaining it, but I do think there’s something to it.

        The biggest issue I ask is “how hard is it to debug if the abstraction doesn’t work or does something unexpected?”, which is kind of a different way of phrasing “How easy is it to understand?” Sometimes this is very easy and there is no problem; other times it can be very hard, and this is when you run in to trouble.

        There are a few factor that influence this: assumptions that the abstraction makes, interactions with other parts of the system, or just being too clever for its own good.

        1. 4

          Abstractions always work in the service of some greater good, otherwise there’s no way to judge whether they’re appropriate or not. So here’s your test: measure how much time users spend trying to understand, use, debug your abstraction level as opposed to reaching their own goals.

          It’s a simple measurement, no Scotsmen required.

          1. 3

            This seems to take the argument posited in bad faith.

            Magic often involves twiddling some arcana that has little correspondence to the task at hand. Whereas an abstraction should let you peel it away when necessary, and signal to the reader that something more is going on.

            Smart pointers in C++ fall firmly into an abstraction because they always let you get a raw pointer out while doing what they purport to do. A more magical construct of them would likely not allow raw pointer access after they’ve been constructed, forcing users into casts and state tweaking to access them.

            It’s not the best example of magic, mind you. But magic typically requires multiple lines of comments, whereas abstractions flow better due to less conceptual context switching.

          2. 4


            Magic is different. It feels different.

            1. 3

              This feels like the same thing as Simple vs Easy by Rich Hickey. Even if you don’t care about Clojure, it’s a great watch and I wish there was a better text summary or explanation available.

              1. 2

                The subtle distinction between ‘abstraction’ and ‘magic’ is a bit like the famous quip about porn: you know when you see it. It’s too subjective and fluffy to be of use. I think a better way to decide if something has gone too far is the Principle of Least Surprise: if an abstraction introduces unexpected side-effects then it’s a bad abstraction.

                1. 1

                  I think it’s objective enough, or can be formulated as such. Let me suggest some easily identifiable objective metrics (with relation to the text below):

                  • “it works when you run it, but not when you read it”

                    Magic is different. It feels different. When you encounter something magical, it may work, but you aren’t sure why. Worse, you can’t trust it.

                  • Abstraction inversion

                    A successful abstraction is presented as a set of tools. There may need to be escape hatches, and a good abstraction includes semantics around them that, if not elegant, are at least usable. The places where “leaks” occur are signposted.