I'm really annoyed by this kind of things like they have any meaning except "I don't know shit about how abstractions work in computers"
%s/you/picture author
I'm really annoyed by this kind of things like they have any meaning except "I don't know shit about how abstractions work in computers"
%s/you/picture author
You even mentioned it yourself: this is the additional overhead added by the Java ecosystem on top of what may already be a pretty large stack of stuff. (From what I've seen, libc is pretty shallow. The majority are either leaf functions like strlen(), or <5 levels from a leaf/before execution disappears into a system call.)
Even if the overhead is not much to a machine, it is certainly going to have an effect on the human who has to figure out what's going on. When you write such code you may not think this way, but every piece of code is a possible place for a bug to be.
From my short experience with Enterprise Java years ago, such deep callstacks are usually symptomatic of code that does far more indirection than actual work --- hundreds of tiny methods that have maybe a statement or two and then call another one. I understand that it might feel good and even better to write code like this, but that simplicity is deceptive: you aren't looking at the whole picture and just focusing on micro-simplicity, when it's macro-simplicity that really counts.
This means that when you're trying to track down a bug, the "interesting parts" are scattered in tiny pieces across dozens of files, and it increases the cognitive overhead significantly in having to piece everything back together. You might not realise that something is wrong until you're deep inside, and then you have to jump back several levels above in the callstack to figure out where things went wrong, seeking in and between files to trace out the execution flow.
I'm glad I don't work on code like this anymore.
This isn't just a Java thing. I've seen plenty of TDD code written with other languages where there is basically the same thing (so the code can be "testable"), but also with heaps of redundant tests that just add dependencies and maintenance overhead without providing much value.
I'd wager that the JIT in OP's picture will transform that huge call stack into something <5 levels from CPU instructions/syscalls.
What remains though, is the huge context carried around such a stack; and I'm quite excited at what the Loom project [1] could yield to tackle that.
Such huge call stacks are a good sign to me. I don't want to implement all the corner cases of all the RFC I rely on; and it is great that library implementors can organise their code well. In the end the JIT will adapt my program to the currently used corner case, and prune everything else.
[1] http://cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.htm...
"Java EE is a lot about abstractions which I have grown to appreciate over the years. However, I find this very difficult to explain to my colleague who sits just across the room – he is a Mainframe veteran with tons of experience :)"
In my experience, the code you write within a third-party framework rarely works the way you expect it to the first time. So you often do "debug" the framework, just to see where your code goes while in the framework to see what you missed.
That's not a bad thing, though--it's a great way to learn any new framework!
There is no sane alternative to this...that is the power of open source where we collectively pool our capacity into something worthwhile to everyone. I want more of it.
Two function calls for business logic, a hundred dependency libraries, thousands of lines of stateful "initialization" code outside of the stack and a fuckzillion XML files for configuration. But yeah, everything's great!
And instead you want to switch to what? Writing 20 lines of that same logic + implementing stuff that has already been better implemented in all of the libraries, by better programmers (because they were able to focus and specialize on that one thing)?
For what? A 2% increase in wall-clock speed? There is a 1 in a hundred applications where this matters. What matters in others is maintainability and development speed. Development is expensive. CPU time is cheap.
The opposite is not, and should not, necessarily that you write your own versions of things; after all, if you did so, you'd still have as deep of a stack trace as you do with the third-party code!
I guess, if I encountered this stack trace, I might say there's something of a code smell there. But, hey--this is also fairly old code, and I suspect things are Better Now, so let's not be so quick to cast stones!
Object Oriented Programming has been mostly replaced by Annotation Oriented Programming in modern Java web development.
These can often be very powerful, but of course debugging is more complicated.
Annotation Oriented Programming arose out of Java's lack of powerful and general abstractions like first class functions. Lambda syntax for "single abstract method" interfaces has largely addressed that problem, but the use of annotations as the primary higher order abstraction mechanism remains in Spring and similar frameworks.
Lisp macros are part of the day to day tool set of every day Lisp programmers. They should be used with restraint, but can be debugged and analyzed with the same development tools and skill sets as other Lisp code. Java annotations belong much more firmly in the realm of framework authors. Writing Java applications often involves using annotations, but very rarely involves creating annotation and generating code or new behavior based on them.
Only if macros are second-class relative to built-in primitives in terms of debug support.
E.g. typical C compiler and debugger not knowing anything about C macros; if we make a loop construct entirely in a C LOOP(...) macro (spanning many lines of the source file), any error within the statements and expressions enclosed in LOOP will be reported against LOOP's line number. Moreover, we will not be able to step through those individual expressions in the debugger.