Tis a trick question. The actual complexity experienced by humans depends on the job they are trying to do with the object. If you are a botanist/arborist driving to work, then clearly it is the apple. If you are a mechanic on your lunch break, then clearly it is the engine.
It's true that abstractions can lead to a lot of trouble when they are failing to work correctly. But when they are good they add a lot of good value. When did you last have to debug a system call, because you couldn't open a file? There are really good, old abstractions that no one thinks about anymore, since they're just really good.
A framework in web development is of course another matter, where complexity can sometimes be staggering. I believe that the lifecycle of React components is really nice. Instead of initialising X things you can think about how a single component will change over time.
There are endless examples of good and bad abstractions, I guess. But at the end of the day, we have to choose abstractions when we need them to reason about what we're doing, despite adding more points of failures.
After all, if an abstraction just enables us to think faster and more clearly about a problem, AND allows us to write less code, that means that less bugs will be in the final product, assuming that the density of bugs is constant. ;)
Proper abstractions are supposed to compress large bundles of information into a smaller set of [broad] concepts, which then imply all the large sets of details they're abstracting away. High level languages are pretty good abstractions on top of raw assembly code for example, because you don't have to think about what the assembly for your java code is going to look like, it's already implied that it's going to work.
As for react, I'm also not a huge fan, but that's because it's still a relatively leaky abstraction that comes with a lot of the same baggage as plain js+html+css. Especially when compared to a fuller abstraction like Elm[0]. But even that's still lacking a complete abstraction of html/css stuff. And that's not even taking the leaky abstractions on the backend side of the equation into account:
http://haskell-web.blogspot.com/2014/04/towards-deeper-integ...
Ref: https://medium.com/javascript-scene/what-is-webassembly-the-...
* DOM,
* HTML Layout,
* CSS,
* A small variety of crappy events ( onDomLoaded ),
* a fairly rubbish heavyweight communication layer ( AJAX )
* Keyboard bindings that never work cross app, never cross platform
* Accessibility ??
* No idea of the energy impact of your code
* No ability to do queries on important device metrics ( energy consumption, clipboard, user presence, login / logoff, single sign on )
* History management and back button mess
The problem that haunts me -and a lot of other hackers I presume- is that we have this urge to understand every little detail of the systems we design. Nothing is good enough.
The other problem is that by using arcane and primitive tools we limit ourselves in the ability of producing real work. As with everything in life, we need to make the distinction between tinkering and doing real work.
If all we do is tinker, very little gets done :).
PS: I just read the satire Real Programmers Don't Use Pascal [1], my comment could be highly biased from that.
[1]: http://www.pbm.com/~lindahl/real.programmers.html Bonus: Here is a little bookmarklet I wrote for styling unstyled html pages for easier reading.
javascript:(function() { document.body.style.maxWidth='700px'; document.body.style.margin='0 auto'; document.body.style.lineHeight='1.5'; document.body.style.fontSize='20px';} )();C -> ASM / byte code -> machine code -> primitive CPU operations
All these javascript and css transpilers however, they force you to know the higher level, the lower level and many of the intricacies of how one translates to the other.