dev-tooling

ericdallo 2025-09-12T13:47:01.897969Z

At Nubank we are investigating why devs complain about cider/calva debugger not working during their debugging, after some research and tests, me and @arthurfucher the major issue we found for us was that cider-nrepl seems to override/lose #dbg meta on functions when using load-file . Not sure if that's a design decision but people (including us) heavily rely on load-file when coding, avoiding to eval specific functions, just the file and things will just work™, but with that limitation (or not having a option to allow that), makes debugging experience really unfeasible most of the cases for complex codebases, WDYT? Do you see any way to improve/workaround that?

borkdude 2025-09-15T09:48:49.872999Z

When I use the CIDER debugger and I'm seeing an inline value printed, can I get it to work together with cider-pprint?

borkdude 2025-09-15T09:49:06.851339Z

as in, cider-pprint-eval-last-sexpr or so?

borkdude 2025-09-15T09:49:46.891189Z

o wait, it even says it at the bottom: cider-inspect-last-result

borkdude 2025-09-15T09:50:48.523479Z

which doesn't work for me. it just takes a very long time and nothing happens

borkdude 2025-09-15T09:50:56.897009Z

timed out it says

oyakushev 2025-09-15T10:10:49.419949Z

Let's move this discussion to #cider please, shall we? We are far away from the original topic now.

borkdude 2025-09-15T10:11:50.660719Z

yep.

2025-09-14T20:46:47.889169Z

The issue seams to be in how cider.nrepl.middleware.debug/maybe-debug works. It reads the code with a fake reader to determine if there is a #dbg tag. In the single form eval, the code is #dbg (defn some-code [...]...) which makes has-debug? true. But in the load file case the code is :

(try (clojure.lang.Compiler/load
      (java.io.StringReader.
       ((clojure.core/deref (clojure.core/deref (var nrepl.middleware.load-file/file-contents)))
        (quote ["... CODE_FILE_PATH ..." G__23022]))) "... CODE_FILE_PATH ..." "... CODE_FILE_PATH ...")
     (finally (clojure.core/swap! (clojure.core/deref (var nrepl.middleware.load-file/file-contents)) clojure.core/dissoc (quote ["... CODE_FILE_PATH ..." G__23022]))))
which makes has-debug? false because that code doesn't have a #dbg tag

2025-09-14T20:50:41.067789Z

those FlowStorm screen captures should make it more clear

2025-09-14T20:53:44.115619Z

not sure about a clean way to fix this, but this fake-reader trick needs to be applied to the file code reading somewhere before eval happens so we can add the :eval cider.nrepl.middleware.debug/instrument-and-eval to the message

ericdallo 2025-09-14T21:30:04.502739Z

Oh that looks like a really good explanation, thank you a lot @jpmonettas that should help maintainers

❤️ 1
2025-09-14T21:39:58.470929Z

I just went and recorded what I did in case someone finds it useful https://www.youtube.com/watch?v=sHGnb70xosk

❤️ 3
borkdude 2025-09-14T21:45:27.821779Z

I've only watched the first minute and thank you for showing how frigging easy it is to use the CIDER debugger

borkdude 2025-09-14T21:45:39.268609Z

(going to watch the rest now)

2025-09-14T21:48:50.443969Z

haha thanks @borkdude. If you learn to use the cider-debugger, you learn for free to use the elisp debugger. They are almost the same, with the same keybindings. Just C-u C-M-x to "eval instrumented", and then when the function run it should show the commands (n for next, etc). Then re-eval it to get rid of the instrumentation.

2025-09-14T21:51:23.225279Z

Although now I think I showed something different on that video, which is to write the #dbg key. I never used it like that tbh

borkdude 2025-09-14T21:52:08.536159Z

yes, I find the #dbg much easier since I always forget this instrument keybinding

👍 1
oyakushev 2025-09-14T21:52:17.725009Z

It's still good to know about #dbg because it doesn't have to be written at top level, but anywhere in the form.

👍 1
borkdude 2025-09-14T21:53:14.806609Z

I'll be using #dbg . last time I used the cider debugger was in 2018 and having to look up a keybinding is enough friction for me to just not do it, but I can remember #dbg. It's stupid how these things work ;)

oyakushev 2025-09-14T21:53:37.854309Z

@jpmonettas This is yet another very impressive demo. I really must overcome the inertia to get into Flowstorm.

❤️ 1
borkdude 2025-09-14T21:53:49.280809Z

yes, I agree. impressive!

❤️ 1
bozhidar 2025-09-15T05:17:47.547399Z

> I’ll be using #dbg . last time I used the cider debugger was in 2018 and having to look up a keybinding is enough friction for me to just not do it, but I can remember #dbg. It’s stupid how these things work 😉 For me it was easy to remember the keybinding as it’s the same in edebug and SLIME. In general most core keybindings were lifted from elisp-mode and SLIME, but I guess for people who don’t use them much they might seem pretty random.

bozhidar 2025-09-15T05:19:18.796349Z

@jpmonettas Can you add your findings to a GH ticket so we can discuss our options for a solution there? It’s nice to see someone willing to help with the maintenance of the debugger!

👍 1
ericdallo 2025-09-12T13:47:57.635629Z

c/c @bozhidar @alexyakushev

pez 2025-09-12T13:51:05.724829Z

Is it when functions are instrumented in the repl, or does literal #dbg meta in the file also get nuked?

ericdallo 2025-09-12T13:51:27.221779Z

I believe it's the instrumentation indeed

pez 2025-09-12T13:52:14.954149Z

So then it’s not specifically about load-file, but rather that the functions get evaluated at all, I am thinking.

ericdallo 2025-09-12T13:52:17.528689Z

I mean, when using load-file the function is reevaluated and it's like it had no dbg inside it, it just ignores it

ericdallo 2025-09-12T13:53:02.717629Z

If I add a println to the function and use load-file the function is reevaluated and the println starts to work, so I wonder why #dbg doesn't work

2025-09-12T13:53:45.136809Z

@pez is related to the behavior that is described also in the Calva docs (https://calva.io/debugger/#loading-the-file-and-eval-on-save)

pez 2025-09-12T13:54:07.040819Z

So, then you have #dbg in the function definition and gets removed?

👍 1
pez 2025-09-12T13:54:49.707179Z

That doesn’t seem right to me.

ericdallo 2025-09-12T13:55:04.032059Z

gets removed == unset, the code is there yet, it was just ignored

ericdallo 2025-09-12T13:55:30.389929Z

yeah, we thought the same @pez but maybe it was a design decision, but sounds really weird to the point people think that debugger doesn't work hehe

oyakushev 2025-09-12T13:55:51.082679Z

eval and load-file use separate code paths. The latter just feeds the file contents to Compiler/load , so it has no special treatment for #dbg and similar reader macros.

pez 2025-09-12T13:56:07.570019Z

Ah, that makes sense.

oyakushev 2025-09-12T13:56:30.541019Z

This has been a design decision from early nREPL days (`load-file` existed in nREPL from day one, I believe)

ericdallo 2025-09-12T13:56:53.965199Z

I see @alexyakushev, thanks for explaining, wouldn't makes sense to make that code be instrumented as well?

pez 2025-09-12T13:57:00.278349Z

Then it should work re-evaluating the whole file instead of using load-file.

oyakushev 2025-09-12T13:57:25.882599Z

A fix would have to introduce an alternative cider/load-file that feeds forms into the evaluator one by one, and teaching CIDER and others to use this op when it is present. It is feasible, just needs a bit of work

borkdude 2025-09-12T13:57:29.881939Z

[side remark] damn, I should start using the debugger some time again. I'm sure it'll beat my manual println approach 😆

😂 5
oyakushev 2025-09-12T13:58:21.508859Z

In fact, there's nothing that Compiler/load does that we can't do ourselves on form-by-form basis.

👀 1
ericdallo 2025-09-12T13:58:23.881179Z

@borkdude exactly, the debugger is so powerful IMO, but thousands of devs at nubank still use #nu/tap (a colored println facepalm), that's what we trying to improve

pez 2025-09-12T13:58:41.957849Z

It sometimes is better than inline defs, @borkdude, but most often not.

oyakushev 2025-09-12T13:58:54.868829Z

Let me take a look at it, I'm annoyed by C-c C-k ignoring dbgs too

💜 1
ericdallo 2025-09-12T13:59:20.228169Z

@borkdude especially when you wanna navigate stacktrace, see complex values in nested stuff and lots more

borkdude 2025-09-12T13:59:29.737109Z

btw if you use load-file or load-string it seems data readers do still work.

(set! *data-readers* {'foo (fn [x] [::foo x])})
user=> #foo 1
[:user/foo 1]
user=> (load-string "#foo 1")
[:user/foo 1]
user=> (load-file "/tmp/foo.clj")
[:user/foo 1]

oyakushev 2025-09-12T13:59:35.100599Z

I just don't want to change nREPL's original load-file behavior just in case, but a new custom cider-nrepl op is a fair game.

borkdude 2025-09-12T13:59:39.435429Z

oh yes, inline defs I use a lot.

2025-09-12T13:59:49.942029Z

and for users like me, that has feature like "load file on save" is really annoying 😅

☝️ 1
😱 3
borkdude 2025-09-12T13:59:57.832349Z

what about manually using load-file though instead of the cider OP?

oyakushev 2025-09-12T14:00:17.273149Z

> btw if you use load-file or load-string it seems data readers do still work. #dbg data reader doesn't do anything by itself, it is a special eval "mode" that does the instrumentation, #dbg is only a tag for it

👍 1
borkdude 2025-09-12T14:00:22.299939Z

oh right C-c C-k, I use that a lot

borkdude 2025-09-12T14:00:57.477509Z

one last side remark question: have you guys tried flowstorm and how does it compare to cider debugger?

borkdude 2025-09-12T14:01:09.830479Z

(I have only briefly tried it but not enough to get it working)

pez 2025-09-12T14:01:28.683749Z

flowstorm is awesome. Works in ClojureScript too.

pez 2025-09-12T14:02:17.038299Z

But I tend to just use inline defs, often with the help of Snitch.

ericdallo 2025-09-12T14:02:27.723259Z

I tested flowstorm, it's awesome indeed, but I see as a different extra tool, would be really nice if things work out of the box for editors too

👍 1
oyakushev 2025-09-12T14:02:38.385049Z

I haven't tried it, but I'm always apprehensive about using such tools that unpredictably increase computational and memory footprint of runtime stuff. At least, with CIDER debugger, I know it won't blow up my stuff (and I still know better not to instrument hot multithread-called functions)

borkdude 2025-09-12T14:02:59.288509Z

ok thanks, I'll give the cider debugger another shot

pez 2025-09-12T14:03:29.035729Z

If you like it enough, maybe Babashka nrepl gets support for it.. 😃

😯 1
oyakushev 2025-09-12T14:03:42.402339Z

CIDER debugger is good for one-off debugging, but I still use inline defs too.

2025-09-12T14:04:03.826649Z

I just tried the "tutorial" of FlowStorm, and seems that it is more to record a flow and understand things.. than for a debugger that I'm really evaluating the lines and going with the debugger

☝️ 1
borkdude 2025-09-12T14:04:12.756949Z

I actually want to go another direction with babashka.nrep. I want to make stuff more compatible with the CIDER nREPL stuff so it just works from source

❤️ 1
pez 2025-09-12T14:04:48.627749Z

Sweet. I didn’t wish for any particular direction to take it, but that sounds super awesomest.

oyakushev 2025-09-12T14:05:21.944349Z

> If you like it enough, maybe Babashka nrepl gets support for it.. 😃 I don't doubt Michiel's capabilities one second, but CIDER debugger is really quite a complex thing. Nobody but @malabarba ever understood it. Don't know if it's even worth the complexity of trying to reproduce it.

pez 2025-09-12T14:05:29.774379Z

Of course, I want it to work in Joyride too, so then it will be some special thing, I guess.

ericdallo 2025-09-12T14:05:48.584259Z

@alexyakushev yeah, that's something interesting to think about: most experienced devs know about those inline defs hacks and things like that, but IMO they are just workarounds of a debugger experience. Most nubank engs that come from other languages feel the need of a debugger like java, so I believe offer the same capabilities/xp would be awesome

borkdude 2025-09-12T14:06:06.395379Z

I'm not surprised that it's a complex thing, but what I would expect is that it's "just" a tools.analyzer like analyzer + pass + emit

pez 2025-09-12T14:06:34.951999Z

To me the debugger is a workaround when inline defs get a bit involved with conditional logic.

oyakushev 2025-09-12T14:06:59.083669Z

It doesn't use tools.analyzer, but you can probably implement it using it and that could simplify things

pez 2025-09-12T14:07:06.402579Z

Stopping the program to debug it is a bit uncivilized.

💯 1
borkdude 2025-09-12T14:07:38.667489Z

at this point we're ok going off topic a bit? @alexyakushev does the CIDER debugger support 1.12?

borkdude 2025-09-12T14:07:56.225439Z

I mean the method value syntax etc

oyakushev 2025-09-12T14:08:04.845129Z

@ericdallo Nah, I definitely agree the OT debugger bug should be fixed and we will do it. Just saying that even as a CIDER debugger ambassador, I still use defs quite often.

borkdude 2025-09-12T14:08:21.429649Z

for clerk I migrated away from tools.analyzer in favor of our own analyzer that does support new features. tools.analyzer lags a bit behind

oyakushev 2025-09-12T14:08:51.324689Z

Yes, like I said, it doesn't rely on tools.analyzer, so it works with all clojure versions

👍 1
ericdallo 2025-09-12T14:08:58.619819Z

@alexyakushev got it, thank you so much!

borkdude 2025-09-12T14:09:18.455859Z

I realize now that most people in this thread are coming to the conj. Are you coming too @alexyakushev?

oyakushev 2025-09-12T14:10:18.317989Z

Hah, I would love to, but I'm stuck in this little thing called "war" and "military" for the past few years 😅

borkdude 2025-09-12T14:10:40.487439Z

I could have guessed. Hope it will be over soon. Are you serving?

❤️ 1
oyakushev 2025-09-12T14:11:01.396559Z

Yeah, since 2022

🇺🇦 3
❤️ 7
borkdude 2025-09-12T14:11:17.439689Z

Thank you for protecting your country but also Europe!

🇪🇺 2
🇺🇦 4
bozhidar 2025-09-12T14:28:55.058709Z

> I don’t doubt Michiel’s capabilities one second, but CIDER debugger is really quite a complex thing. Nobody but @malabarba ever understood it. Don’t know if it’s even worth the complexity of trying to reproduce it. And Artur became a Ruby dev in the end, like me. 😅 We used to work in the same company for quite a while.

borkdude 2025-09-12T14:29:37.225819Z

@bozhidar weren't you already doing Ruby before Clojure?

bozhidar 2025-09-12T14:32:10.715499Z

Yep. In a way I’ve always been doing Ruby in the past 15 years, at least as far as jobs go. (I’m mostly a manager these day on my job, though). Clojure was always a passion that I didn’t manage to convert into a career.

😿 2
bozhidar 2025-09-12T14:34:31.062929Z

> This has been a design decision from early nREPL days (`load-file` existed in nREPL from day one, I believe) Indeed. I believe the reasoning behind this was a combination of efficiency and easy of getting the source metadata when evaluating an entire file. I wasn’t part of the original design discussions, but I discovered many things while working on CIDER and afterwards when I took over the maintenance of nREPL.

bozhidar 2025-09-12T14:38:04.911269Z

Btw, I’m not sure how many people know this, but the debugger in its current version is heavily inspired by edebug itself. The first version of the debugger used the Java debugger APIs, but Artur realized the approach taken by edebug was a lot simpler and almost independent from the underlying platform.

ericdallo 2025-09-12T14:39:47.634969Z

That's really interesting to know, especially that used java debugger API

borkdude 2025-09-12T14:42:42.134579Z

@bozhidar care to explain more - what does edebug do?

ericdallo 2025-09-12T14:43:26.949379Z

It's a Emacs/elisp debugger @borkdude, really nice

bozhidar 2025-09-12T14:49:04.106739Z

There was a nice talk on the subject from the last Clojure event I ever attended. I planned to do an expanded version later in 2020, but it wasn’t meant to be. 😅 I’ll dig up the talk in a bit.

borkdude 2025-09-12T14:50:19.168099Z

nice!

bozhidar 2025-09-12T14:51:38.201149Z

Here it is https://drive.google.com/file/d/1YhnPBJOXbUzXfVOEojICFTwhCt_3u6Ss/view It’s from a contributor to the debugger, but I helped a bit with the content and I think it’s probably the deepest dive into the debugger’s internals we ever did.

borkdude 2025-09-12T14:56:22.351909Z

cool, where was this presented?

bozhidar 2025-09-12T14:58:42.904119Z

It was either at IN/Clojure 2020 or a Clojure meetup that took place in Pune right after the conference.

bozhidar 2025-09-12T14:59:02.111429Z

The debugger hasn’t changed much since then, at least as far as main design decisions go.

borkdude 2025-09-12T15:00:07.936329Z

right

borkdude 2025-09-12T21:05:30.719999Z

nice presentation!

❤️ 2
2025-09-13T19:35:51.350059Z

> I haven't tried it, but I'm always apprehensive about using such tools that unpredictably increase computational and memory footprint of runtime stuff just to clarify, it shouldn't be unpredictable, you can control instrumentation and recording with a couple of buttons and you can also establish heap limits for auto stop recording

2025-09-13T19:38:58.655469Z

> Nobody but @malabarba ever understood it. Don't know if it's even worth the complexity of trying to reproduce it. I can probably help with cider-debugger stuff, since vanilla FlowStorm is basically the same code as cider-debugger but recording instead of stopping and waiting for a "next command"

3
❤️ 6