Similarly, at least in imperative languages, I made the experience that people who develop with a debugger write less testable code then people who do the debugging in their code, for instance with assertions or test frameworks. Sometimes I feel these two groups are disjoint.
Repls can only give you feedback one code line at a time (and if you type a procedure/function into a REPL, you get no feedback at all until it’s called in the main loop), it’s ironic they are found more commonly in in expression-based rather than statement-based language.
Three that bolted on test coverage requirements at some point did so after the same catalyst. An unharmonious developer joining the team and subjects old, stable code to his opinions. "JQuery is the devil!" The developer cries, as he slashes and burns all the script tags into dust.
From there, he installs <his opinion> into the toolchain and spends a tireless 5 hours getting things to sort of work. "That's so much better." he remarks to no one as he scissor claps the dust off his hands.
The next day, users wake to a world in tatters. -- they submit tickets, pleas to an angry god to return the serenity of the benign that existed a mere day ago.
Rarely at this stage is time simply rewound - though it would be easy to do, someone will remark once. The conquerer of the old tech campaigns hard for hearts and minds. He's baffled that he is not greeted as a liberator!
"Spaces are superior to tabs, the fact that the code broke after I ran a regex replace is mostly python's fault. Stack overflow said that people who prefer spaces earn more so it's basically a good business decision to move on from here."
For some reason it's easier to be righteously indignant about destruction than maintenance. The strength and intensity of the usurper tends to triumph. Besides, client side validation should be easy to fix -- and there it is. The rest of the pantheon has been cowed. Cowed by the illusion of a few softball one point tickets ahead of them.
Unfortunately, glass shelled ecosystems created amidst the layers of code sediment don't fair well against the tremors of sed. Each softball one point ticket begets two 3 pointers, and so it snowballs.
When the unharmonious one is summoned by the pantheon to help fight back the cascade, he almost always bravely calls for a complete rewrite. "This is due to the fragility of the legacy system." he always reasons.
This is about the time that the descent into madness is halted by a call for test coverage. "We've experienced a lot of regressions recently, we should probably start writing tests."
This is also about the time our regicide leaves for another company.
I'm getting the impression the underlying message is that in most common situations a high test coverage is often overrated? This would also be my impression after working on dozens of projects
It is possible to go lower level and just throw gdb at it but a lot of compiled functional code depends on multiple source locations and dwarf can't represent that.
On the other hand, for pure code the distinction between repl, debugger and test suite kinda blur anyway.
This is not to detract from your point. I have gone further and lamented the death of debuggers. Primarily because I have seen them used so rarely in the wild. That combined with the somewhat ironic rise of functional idioms in java that are actually difficult to debug. (My understanding from last time I asserted this, is just that I don't see them used much.)
I agree that functional programming idioms in practice makes debugging harder, but only because debuggers lack the right UI to deal with the buried control flow of things like functional orgami. I've heard F# with its Visual Studio plugin is doing better in this regard.
JavaScript has the advantage of the debugger keyword. Has the disadvantage of the execution engine not being hooked up to the editor for most folks. I have seen some changes to that. And I am still enamored with skewer mode. (Haven't used it much.)
Java debugging blows my mind. It was trivial to do and I recall tons of people doing it in school. I have yet to see anyone at the last few offices I've worked in do it.
Worse, many common designs and practices make it much more difficult than it needs to be.
The biggest issues I've had with web browser debuggers is the interaction(or lack thereof) between source maps and debugging. I don't know any of the modern debug features that the browsers are missing - could you mention some?
E.g. Linus Torvalds doesn't use a debugger whereas John Carmack relies heavily upon a debugger.
I'm sure Linus at least uses a kernel debugger. But looking at https://en.wikipedia.org/wiki/Kernel_debugger, I guess not.
It has the major advantage of the system evaluating the code is easily understood when stepping around, though.
https://www.youtube.com/watch?v=pkjFTdq2q2g
It makes use of Cincom.
People who unit test or don't is more correlated with age and commercial experience / maturity level. Although those correlation curves aren't monotonic either.
People who don’t use the tools seem to trust their own eyeballs more than they trust code. Even as someone who spots bits of code that nobody else sees, I don’t trust my eyes that much. It’s not a matter of if I can spot things, because I can, but it’s not a reliable skill.
It’s a matter of reliability and a matter of saving that energy for the next step.
I, personally, don't trust source-level debugging with gdb, for example.
I really appreciate some aspects of both styles, but I am not a die hard either way, my limited experience with functional programming has never lead me into the depths of debugging like discussed in the article. So the article actually makes me wary of going further.
I would liken this effect to a form of technical debt similar to a framework. Upfront development is faster, but when there's a problem, it's much harder to fix or work around, and problems can be more obscure.
Is this concern valid?
I also remember Racket having pretty powerful debugging capabilities, including the ability to step through programs and see how each expression is evaluated.
Some languages have worse tooling than others, but I don't think it's a problem endemic to functional programming.
Here's an example functional debugger that can step forwards and backwards in time, as well as modify code at any time:
I have looked and large and hairy C++/Java/C#/Erlang/Python programs. I think overall Erlang are the easiest to debug. Two reasons are: state is most likely to be passed in explicitly, and data is immutable.
Compare to, say, looking at a class instance in an OO language, which inherited from a bunch of other classes, and then had some methods called on it, modifying its internal state, and now you're and asking yourself "hmm, this seems broken, but how did we even get to this point?". vs looking at function like "update_state(State, NewData, ...) -> ... NewState.". In the second case it is clear what is happening, what the old state was, what the new is. If the bug happens on a live system, it is easy to attach a trace that would trigger only when NewData matches something or old state matches, or both match.
Please fill out the feedback form if you have time, so we can improve the project.