
JavaScript debuggers are broken - mistersys
https://samdesota.com/2019/04/21/javascript-debuggers-are-broken.html
======
Sawamara
Some of the comments here are surreal, I tell you. Surreal.

I use chrome's built-in debugger to debug compiled down typescript code. I go
to the original TS files, mark a line by conditional breakpoints, it works
perfectly. It stops the execution of the compiled JS files, it uses the source
map to find the original line (which I placed the breakpoints in) AND it has a
perfectly fine, working stack trace as well at pause. I can also blackbox at
will.

Timetravel debugging is going to happen soon (with its proper costs), and
thats about it. Variable watching is the only pain point, but that is only
because TC39 axed the native observable implementations a few years ago.

I would not respect anyone who calls this level of debugging support "bad". It
can be improved, but it is satisfactory.

~~~
Jasper_
I have had a similar experience... perfect debugging, breakpoints working,
everything fine, up until yesterday. I think something in my stack got
upgraded, and ever since, exceptions have broken callstacks. Trying to click a
breakpoint takes me somewhere completely else in the sources viewer, and
sometimes, exceptions don't break at all. No, I don't know why. Something
broke, I still need to investigate, but it's another annoyance that reminds me
of how much of my time is going to debugging and fixing platform issues.

I think the big issue is just how fragile and brittle source maps are. They
feel like a joke, compared to DWARF and native debuginfo.

But who knows, maybe they'll start working again tomorrow.

~~~
Sawamara
That is frustrating, I concede on that point. (If there is one thing that is
bad about the JS ecosystem, its the brittleness and complexities of setting up
build tools. If you get together one that suits your need, you do not look at
it again unless an update breaks it. Then the process of looking it up how
_exactly_ it works comes again, and this repeats yearly :P)

------
saagarjha
An interesting trend I've seen is that interpreted languages often lack a
debugger, as most "prototyping" ends up happening in a REPL, which act as sort
of an interactive debugger with each prompt serving as a sort of "barrier"
where each value is guaranteed to be reified and execution is not in some
awkward place. Whereas with compiled languages you have debuggers that "work"
because there's a consistent model of how execution proceeds and getting
things like a stack trace is easy, allowing for things like placing
breakpoints on lines of code or "executing" arbitrary code in essentially any
context. Starting out with compiled languages myself, I find it really
interesting to watch how other programmers structure their code in Python or
JavaScript code to make it introspectable in the face of arbitrary reflection,
asynchronous callbacks, etc.

~~~
Jach
"Interpreted" is probably the wrong word, "interactive" would be my choice.
But I think I agree with your overall point, that when you have a high level
of interactivity, you don't need to rely as much on automated debuggers which
is why you see them missing or barely mentioned -- at least if you are making
use of such interactivity and aren't just trying to write code the same way
you would older Java. I've done a lot of Python without ever feeling a need
for pdb, and didn't even learn about its existence until quite a bit later in
my Python studies, but doing C and C++ necessitated learning about gdb (and
valgrind) very early on. For Java, it's a mixed bag for me -- I can get by
fine with pure reason (and some logging and test code) and vim up until a
certain project size or contributing coders size threshold is reached.

Meanwhile you have old dogs like Lisp that newer languages supposedly cribbed
a lot from, yet these newer languages have all seemed to miss things like
having what we think of as IDE features built into the language spec itself.
The Lisp standard requires built-ins like 'COMPILE, 'DISASSEMBLE, 'BREAK,
'STEP, 'TRACE, 'INSPECT, 'INVOKE-DEBUGGER that end users can use on their own
or as building blocks for more sophisticated tools (like Slime --
[https://malisper.me/debugging-lisp-
part-1-recompilation/](https://malisper.me/debugging-lisp-
part-1-recompilation/)) and implementation-defined extensions. I appreciate
the advances browsers have made in dev tools (Firebug was a godsend) but it's
still rather primitive compared to languages where debugging has long been a
staple or was even defined as part of the language...

------
Aardwolf
I think JavaScript debuggers in browsers are AMAZING, it is fantastic to have
such powerful programming tools on almost every computer! Just like BASIC in
DOS computers and similar computers from that era, this is a programming
language available by default almost everywhere. And the debugger tools are
powerful too with inspecting and changing variables, ...

But what did the author expect, when using a programming language that is not
JavaScript, that the JS debugger would show code in that language instead?
Maybe the framework needs to compile to JS that looks closer to the code you
typed then (or else have its own debugger for its language). That's not the JS
debugger's fault but the framework.

Disclaimer: I didn't read past the part where it showed React code and then
showed that the debugger stops in different looking Javascript code instead

~~~
alkonaut
Par for the course for any language and development environment is interactive
debugger in the code you are writing. As the author I don’t care much about
how many translation steps there are from “my” syntax to machine code, all I
care about is that I can debug my own code and watch my own variables.

The grandparents point is (I guess) that if choosing a reasonable language
(such as TS) instead forces you to give up another must-have (e.g interactive
debugging) then something is wrong with the tooling. I read others that claim
they have working ts debugging so it’s certainly possible, but I’ll add one
more par-for-the-course bullet point: it should work out of the box.

~~~
Aardwolf
Well so it's the title of the article that ticks me off, "JavaScript debuggers
are broken"

Isn't instead the language the author is using (JSX?) that's broken by not
providing a debugger for itself or compiling to an easier to debug form?

------
kristopolous
I made a fairly interesting JS introspection tool about 8 years ago ... I used
to use it all the time:
[https://github.com/kristopolous/_inject](https://github.com/kristopolous/_inject)

The idea is that you want to run arbitrary code at some random deep scope in
the code or perhaps run some map/reduce/filter function over every time it
goes in there and find the anomaly ... it was really useful in characterizing
things like itemizing a frequency of failure and other things I guess I
stopped dealing with.

I think it can be expanded upon but never really seemed to go anywhere. (sorry
if the code walk-through article doesn't work well on mobile and is not
complete - I never finished it)

------
Normal_gaussian
Its interesting to see developers talking about and building debuggers whilst
only apparently having experience of one.

EDIT: apparently the following is no longer true

From gdb, going to Chrome dev tools was super painful. I had been used to
writing high quality watch statements. watch x, and break if it is <criteria>
is a very powerful debugging mechanism.

Chrome has "pin this variable and tell me its value every time I pause". This
is overly manual. Its bad enough trying to get the erronous middle value when
looping over a 10k array, never mind a more complex data structure where the
error condition will depend on a parent/less obvious value.

Im not familiar with many debuggers, and I know I'm not a true expert in any.
Id be keen to know what people think are awesome debugger features.

~~~
z3t4

        if(x) throw x

~~~
Normal_gaussian
The reason this doesn't work in the same way is because it requires you to
know where the error occurs.

You tend to operate on a data structure and know that _at some point_ x
becomes y. By placing a watch on x you find out where that point is.

Editing the code does not get you this (bar modern proxies in js).

~~~
plausibilities
if(x) { debugger }

~~~
Normal_gaussian
Play with conditional watchpoints in gdb to understand why this does not make
sense as a reply to the comment explaining why this is not a substitute.

------
perfunctory
Brian W. Kernighan and Rob Pike wrote that stepping through a program less
productive than thinking harder and adding output statements and self-checking
code at critical places. Kernighan once wrote that the most effective
debugging tool is still careful thought, coupled with judiciously placed print
statements.

~~~
sametmax
Clearly the guy didn't have to pull out a working PoC in one week with a
forced uppon JS framework that just changed its public API and not its doc and
that need magic lines just for webpack to not crash.

Or that has to pull data from a manually written csv our client uploads at 3
pm on their managed ftp and an untested REST api designed last month by a one
man start up, using a legacy client lib that only god and a fired engineer
knew how to get to work.

Or that has to be done with a trainee designer that knows photoshop but will
"learn this css thing on the way" , a remote turkish consulting firm and your
boss dog barking next to you because animals are good for moral and we are
agile right ?

Those theories are good in a lab while writting carefully crafted c for the
board you designed the spec for.

~~~
Tade0
This says much more about the work environment front-end developers are
subjected to than anything else.

~~~
sametmax
Or work with a geographer client that wants to integrate his first terrible,
terrible plugin into postgis.

Or contribute to a mess of an open source project that your microservice
happen to depend on.

Or debug your student exercice that found yet a brand new way to crash that
you never heard of.

Or find what are the side effects of your 3am emergency fix during the last
red teaming.

Programming is a vast land.

------
tobyhinloopen
I have an error reporter in my app and it is completely useless. Stacktraces
are usually just starting within some library and never touch app code. I
rarely know where the error started, I just have to guess. It sucks.

~~~
kristopolous
This is excessively common. Somehow modern debugging is essentially as nuanced
as "shit broke". Debugging was far better 20+ years ago under C++ in win32
then it is today - there was working code navigation as well.

I don't know how we have fallen so far back in the tooling.

~~~
ehnto
PHP debugging is fine. Conditional breakpoints, walking up the stack trace,
interactive and in-place code execution at run-time meaning I can run code or
change variables and it applies to the current execution. It's great.

Javascript is a nightmare to debug, but it's probably just as much to do with
architecture than debugging tools. Of course it's hard to debug a dozen tiny
libraries written in ES6, transpiled down to ES5, packed into a module loader
and flung around asyncronously.

JS debugging is fine in native, dependency free code I write. My sense is that
debugging was something a lot of newer developers found out about later in
their education and as such they never noticed debuggability and tooling
wasn't up to par with older tooling.

~~~
kristopolous
Python and perl are good too. I've heard marvelous things about emacs
consistent uniform debugging interface that takes care of the magic behind the
scenes. In so much as my friend just gets debugging as a built-in feature when
he transits around languages without any knowledge of the underlying
invocation and implementation nuances. He has the same keystrokes and
interface in all the common languages. It sounds magnificent. One day I hope
to actually take the time to learn it.

~~~
etatoby
I found it extremely hard to learn Emacs and memorize all the keystrokes. They
made no sense. After some time I gave up. Learning Vim was a breeze compared
to it.

------
tomsmeding
Notable is that such a debugger, that records the entire exrcution and allows
you to go back in time, already exists for compiled languages in the form of a
gdb plugin: [https://github.com/mozilla/rr](https://github.com/mozilla/rr)

~~~
robin_reala
Record and replay is coming to Mozilla’s JS debugger engine:
[https://developer.mozilla.org/en-
US/docs/Mozilla/Projects/We...](https://developer.mozilla.org/en-
US/docs/Mozilla/Projects/WebReplay)

------
tablethnuser
I don't think the author knows the full set of features available in DevTools.
Sourcemaps make the code look familiar. Blackboxing hides the framework stack.
DevTools supports conditional breakpoints so in the article's example you
could put a breakpoint in the onAddWidget line looking for widget.position.x <
0 or whatever. Voila. Reproduce the bug and you have a stack of your own code
that you can inspect and find the issue.

~~~
ihuman
The author is aware of source maps, and has had issues with them

> To add to our list of feedback for Javascript debuggers here’s a couple more
> frustrations you may be familiar with:

> Buggy source maps resulting from bundling and transpilation tools like
> Webpack and Babel that cause placing break points to be unreliable, and
> defined names declared undefined

~~~
fourthark
In my experience source maps usually sort of work in Chrome, but don't really
work very well in Firefox (which TFA mentioned).

------
sametmax
It's not the debugger that is broken, it's the entire JS ecosystem.

It's such a terrible language we spent the last 2 decades fixing its warts,
adding one layer on top of the other. And now you have es7 + typescript + jsx
+ some magic introspection + code splitting + weird non standard imports, all
that turned into regular JS and source maps, in a complex concurrent and async
env that deals with graphics AND the network, 2 of the hardest things to get
right. All that with zero stdlib, multiple incompatible clients, specific
extensions for each store/router/event system you use and hot reload live
pushing code.

And you wonder why it's hard to debug ?

I get that tooling is supposed to make our life easier, but let's be nice with
the poor guys that have to work on the monster that must be usable with that
stack.

~~~
holoduke
The problems you describe are in most languages. Networking and graphics are
quite easy in JavaScript. Nothing fancy or complex about that. Debuggers are
super light weight. Build into most browsers. Async programming is just
another way. Again nothing fancy and complex about it. You just need to
understand it.

~~~
sametmax
E = MC2 is just a regular equation. Nothing complex about it. You just need to
understand it.

------
braythwayt
Given that:

There are two types of code: Code that implements the “domain logic,” and code
that implements various abstractions used by the do an logic.

I suggest that:

We need to be able to both debug the domain logic and the abstractions
implementation independently of each other.

——-

In simpler days, the above was accomplished with feature like “step over,” or,
“run until.” We simply skipped over the lines of code in functions that were
“beneath” the level of abstraction that interested us.

Today, there are many deep layers of abstractions, and sometimes important
things happen within them. Similarly, our data itself is wrapped up in bundles
of abstractions, and sometimes we need a way to view that data as an
abstraction, at other times we need to “drill down.”

We have sophisticated tools for building abstractions in code, and we have a
way of bolting some of that onto the debugger by means of source code mapping.

But that is not enough. We need more sophisticated ways of stepping through
our domain code that understands the abstractions it is hiding. We need to be
able to write a framework or library, and write debugger extensions to go with
it, just as we write extensions for our compiler toolchain.

That way, we can build abstractions that can be “stepped over” in the debugger
as easily as we can write our code in abstractions in the editor.

——-

I think everyone realizes this and is rushing to implement point solutions
here and there, like special cases for async stack traces when you use JS’
built-in async/await. And the author is hinting at an important “special case”
for React.

I suspect that we need a more general facility for meta-debugging, much as
macros are a more general facility for meta-programming.

------
inian
Might help -
[https://developer.chrome.com/devtools/docs/blackboxing](https://developer.chrome.com/devtools/docs/blackboxing)

~~~
ManlyBread
Blackboxing in Chrome has a major issue, it's impossible to blackbox these
"anonymous" tabs that keep on popping up, which sometimes makes debugging a
nightmare.

------
moogly
I don't have huge problems stepping through code in browsers (source maps,
async/await, blackboxing helps a lot), and when I do, I just resort to
console.debug logging, but what I would like to know is why Firefox' source
map support (at least when using Webpack) still is so terrible? And why is it
OK in the Developer Edition of the browser? Why have two different
implementations? Is Mozilla intentionally hobbling the regular edition of the
browser to justify the existence of the Developer version (because other than
the source map support, I don't see the point of the Developer version)?

And then people complain about developers only testing in Chrome, well...

------
jypepin
Javascript is the only language I've worked with that doesn't offer a good
debugger/breakpoint system, and I agree with the reasons/issues stated in this
article.

It makes working with JS (and Node) a real pain in the butt. IDEs such as
VSCode offer integrated debuggers etc. and I wonder if anyone ever was able to
use it for anything more than a few lines of vanilla JS. I can't seem to
understand how those would work.

I compare this to my experience with Ruby, Python, Go and Elixir, and all
those languages have either a great debugger or an alternative that makes
debugging much easier than going through a "guess based feedback cycles" with
JS.

~~~
tokyodude
I guess I must not know what I'm missing. I've used Visual Studio for 15
years. gdb for 10. I have zero issues using the Chrome's debugger. I don't no
what's different about the breakpoints in Chrome. The seem to offer the same
features.

~~~
jypepin
Well my main issue is that most of the time when I put a breakpoint somewhere
it just doesn't work, or if it works it's pretty much impossible to move
forward or backward from line to line because, as stated in OP's article,
going to next line in a React app for example, you end up in the actual React
implementation code instead of being able to separate framework and written
code.

To me, that makes it pretty much unusable enough that I just don't try to use
anymore.

------
umaar
You can "blackbox" certain code in DevTools so it doesn't pollute your
debugging sessions, made a gif of how it works & what it results in:
[https://umaar.com/dev-tips/128-blackboxing/](https://umaar.com/dev-
tips/128-blackboxing/)

------
breatheoften
I like this article -- its accurate and describes real pain points in the
existing debugger experience which _do_ have technical causes that might
ultimately derive from language/ecosystem design flaws.

I'm curious to see what the author's team is building. I'm quite excited to
see language-level adoption of debugger features. It's unfortunate to me that
debugger's are mainly bolted onto the side of language's with (at best)
convention-based 'semantics' baked into the compiler/interpreter
implementation. With the interface to 'debugger semantics' completely outside
the code and with at most a language-level 'debugger' statement that is
basically equivalent to adding a non-conditional breakpoint.

How about a language with a debugger() {} block so that the full logic for
logpoints, breakpoints, conditional breakpoints can be persisted in the source
rather than in inherently non-scalable ide gui -- and specified as part of the
language design?

How about something like debugger() { setRewindTarget("point1") } -- for
capturing a particular program state in a returnable way -- with perhaps some
semantics/heuristics around the behavior of common abstractions which
interface with out-of-process contexts (sockets and files) -- and/or the
ability to specify what you want to happen to those kinds of resources inside
the debugger {} block so that you can quickly create exactly the rewind
behavior you want for a particular task.

How about more dynamic and easily queryable debugger contexts: 'break on first
execution of each line in each function in project (for reading a new
codebase)', 'break on first execution of code changed in last n commits',
'break on code written by author according to git blame' ...

How about features for augmenting print() based debugging. Baked the semantics
needed to support a value-history inspector into the language implementation.
A program should be able to describe 'debuglog(matrix as image)',
'debuglog(object as tree view)' and the _language_ should ensure the program
is compatible with a debugger protocol able to communicate with an appropriate
rendering context capable of displaying these in a useful way ...

------
z3t4
I remember IE5-6 ? Where it only told you there was an error, and you had to
figure out _where_ yourself via alert("this is line 500") Being used to that I
think Chrome dev tools are amazing. I love that you get a call stack on every
console.warn. All those console.log's can be removed in production via
overloading or marking them as pure functions when minifying, as they will
otherwise slow down the app. There's otherwise no cost to having extra info
available in the dev console, besides Firefox debugger being terrible slow
100x slower then the Chrome console.

~~~
tty2300
I wonder if this is the reason that js tends to fail silently or convert types
whenever possible because debugging errors was just too hard.

~~~
goto11
This was Netscapes design. I would guess it was designed like that because it
was the simplest way to handle error conditions. Initially JavaScript was very
simple and didn't even have exception, so there was no way to report errors.

------
Trufa
It's great to know that you're working on this. The tools are definitely
lacking on that regard.

------
s_tech
Certainly not 'broken' but definitely room for much improvement.

------
localhostdotdev
s/Javascript/React (e.g. debugging works fine with stimulus/coffeescript for
instance)

~~~
yebyen
You're not wrong, as far as I can tell.

I'm a rails dev who has some godawful vanilla JS and JQuery in my legacy apps,
that I maintain to some extent. We've largely avoided the quagmire of
component-ized frameworks like React, Vue, keeping our JS footprint as minimal
as possible while other teams in our area have moved ahead timidly, doing Vue
without TypeScript or any build tools.

The one place where I have started moving my ball ahead is by adding Stimulus.
It coaxes me toward Object-oriented (or at least Controller-oriented)
JavaScript with ES6 classes. It has been a major success story for our small
dev group which doesn't have the capacity to go crazy with a new language or
framework, after deciding on Ruby+Rails some time ago. We can use it without
major upheaval, it works with our legacy apps too without build tools, without
much icky feeling, and without even webpack, just plain asset pipeline, while
our internal-only application customer base is able to guarantee we are
serving pages to a baseline of "current supported version of Chrome" that has
given us the guarantee of needed support for ES6 and no concern about legacy
browser support.

And one problem I also don't have, is any issue with the JS debugging support
that is provided natively in Chrome, plain and simply put. I just write a
"debugger" statement where it is needed in the code, ensure I have the JS
console open when it reaches that line of code, and my debugging environment
is roughly as powerful as pry-rails, no problem.

------
goodrubyist
You shouldn't be using debugging in dev tools to help write your code. Do TDD
instead. This should help
[https://www.youtube.com/watch?v=B93QezwTQpI&t=2581s](https://www.youtube.com/watch?v=B93QezwTQpI&t=2581s)

------
coldtea
Money shot: "In my next post I want to take a step back and imagine
programming nirvana… what if we could design a better debugger? I’ll be
talking about the work me and my team at Raft have been doing to build a new
type of debugger and experimental programming experience for building reactive
GUI applications, without the mess."

------
black-alert
When I first learned to code in TurboC some 30 years ago I used the IDE's step
debugger for a while. Although a great tool for learning how your code
executes line after line, I stopped using it after a short while because I
found it to tedious and distracting to go through the code this way. Being
able to set a breakpoint and see the actual value of something I can do in an
instant with logging. I never use the browsers 'debug' tool, tried it once and
got bored already..

IMAO the code I'm working on should live inside my head, that's the place
where I construct and debug. Even when I'm new on a +100Kloc project, I'd
still prefer to read through the code and figure out what's actually going on.

I see so many developers in the JS world spending soo much time and focus on
all those tools; types, eslint, debuggers, testing, etc.. it makes me really
suspicious about whether they have the actual skill to write a good codebase.
I can only be convinced when my code would be buggy and theirs flawless, but I
never see that, it's for some reason always the other way around! Just take a
look at this project (a random pick) with all those (virtual) safeguards,
result: over 3600 issues!!!
[https://github.com/Microsoft/TypeScript](https://github.com/Microsoft/TypeScript)

I mean 3600+ issues, WTF!! And I should be using this tool to prevent issues
in my software? Sometimes it feels like this entire industry is broken.

~~~
etatoby
I agree.

Some “safeguards,” eminently type systems (with algebraic types and inference
if possible) I've slowly come to value a lot, especially when maintaining a
codebase for an extended period of time.

But otherwise, I still prefer to reason over my code, with the aid of at most
a few debug prints / logs. Breakpoints I only use very rarely. Other debugger
features not at all.

But I remember it taking many years before I could build a complete working
picture of the code I was writing (or reading) in my head. Before that, I was
basically horsing around. Thankfully, that was around high school time. By the
time I got into the industry, I already had that skill finely developed.

Nowadays, I don't think many fresh graduates have it, and yet the industry
prefers hiring them, rather than more experienced (older) people. Go figure.

