babashka 2026-07-22

Hello wonderful people! Can anyone share some pointers on how to properly profile and debug bb scripts? I am talking here more babashka specific approaches or pointers. I can run basic profiling on OS side, but interested if maybe people already spent some time and have something to share. For context, I am doing some yak-shaving and migrating some personal tooling scripts from the java -> clojure -> babashka. There is some expected marginal increase in execution time, but also some quite unexpected (and not so marginal) increases. My current "rule of assesment" is that I expect bb to be slower for big data wrangling tasks. So for example: I have a "sameish" script run across 3 "envs" java is ~100ms, clojure is 1 second, and babashka is 1.5 second. This is working timings for me no complaints so far ๐Ÿ™‚. Script itself is quite simple read file from disk compute sha256 digest and output it back. But for the same script when I increase the input size of file from 2MB to 200MB babashka script runtime is in multiple of minutes. And I see two possible solutions here I need to update my "rule" or I need to profile the babashka script executions or something else is happening that I missing. So will be happy for any feedback here. bb script in question is this:

(ns scripts
 (:require [ :as io]))

(defn file-to-a-digest
  [file]
  (let [md (java.security.MessageDigest/getInstance "SHA-256")
        digest-arr (with-open [s (io/input-stream file :buffer-size 8192)
                               dis (new java.security.DigestInputStream s md)]
                     (while (not= (.read dis) -1))
                     (.digest (.getMessageDigest dis)))]
    digest-arr))

(.read ...) on every character is very expensive in babashka since it's java interop. if you could come up with a clojure stdlib function that did the same, then it would be in the same ballpark

I can probably optimize .read though

Try this instead:

(ns scripts
  (:require [ :as io]))

(defn file-to-a-digest
  [file]
  (let [md (java.security.MessageDigest/getInstance "SHA-256")]
    (with-open [s   (io/input-stream file :buffer-size 8192)
                dis (java.security.DigestInputStream. s md)
                out (java.io.OutputStream/nullOutputStream)]
      (io/copy dis out :buffer-size 8192)
      (.digest md))))

Yep. The copy buffer approach is significantly faster in bb. Thank you. Will add it to the heap of stuff to be aware for the bb interop scripts.

interop in a hot loop is just much slower than in a JVM, that's the rule of thumb

And just like that you singlehandedly reduced my yak-shaving time ๐Ÿ™‚ . Thank you.

๐Ÿ™ 1

https://gist.github.com/borkdude/a19d6f12a0292deaf489d1eeb6374597 https://gist.github.com/borkdude/a19d6f12a0292deaf489d1eeb6374597 Feedback welcome. Text isn't intended for publication, so it's not about that, just the technical side of it.

I often use bb just as a launcher for (clojure -X:build ...) I wonder if a cool feature of bb tasks would be you can tell it to auto-map all build.clj public functions to a build task of the same name. For example I have:

{:tasks
 {test (clojure "-X:build test")
  test-vars {:doc "Run specific tests by passing their fully qualified names"
             :task (do (println "Testing with core.async 1.8+")
                    (apply clojure "-M:test" "-v" *command-line-args*)
                    (println "Now testing with core.async 1.7")
                    (apply clojure "-M:test:test-1.7" "-v"
                           *command-line-args*))}
  gen (clojure "-X:build gen")
  lint (clojure "-X:build lint")
  deploy (clojure "-X:build deploy")
  install (clojure "-X:build install")
  update-documents (clojure "-X:build update-documents")
  bump-major-version (clojure "-X:build bump-major-version")
  bump-minor-version (clojure "-X:build bump-minor-version")
  bump-patch-version (clojure "-X:build bump-patch-version")
  release (clojure "-X:build release")
  publish (clojure "-X:build publish")
  publish-patch (clojure "-X:build publish-patch")
  publish-minor (clojure "-X:build publish-minor")
  publish-major (clojure "-X:build publish-major")}}
Maybe if I could do:
{:tasks
 :all-from-build-clj true
 {test-vars {:doc "Run specific tests by passing their fully qualified names"
             :task (do (println "Testing with core.async 1.8+")
                    (apply clojure "-M:test" "-v" *command-line-args*)
                    (println "Now testing with core.async 1.7")
                    (apply clojure "-M:test:test-1.7" "-v"
                           *command-line-args*))}}}

Though it might break shell completion, and I wouldn't want to lose that

You may be able to write a rewrite clj script that adds them or using rewrite-edn

Perhaps in combination with clj kondo analysis

โš ๏ธ ๐Ÿ™ Testers wanted for a feature that will hopefully soon land in bb. It's auto-completions for bb tasks powered by #C03KJHCNZ99 It auto-completes options / arguments and offers automatic --help. Reactions in ๐Ÿงต

This now landed. When you use this, you'll get auto-completions for exec-fns and subcommands. You can try this with the dev build:

bash <(curl )  --dev-build --dir /tmp
;; bb.edn
{:tasks
 {:cli   script.cli/defaults        ; parser options for every CLI task

  build  {:doc  "Build it"          ; a plain task: untouched by any of this
          :task (println "building")}

  dev    {:exec-fn dev/run}         ; a handler: options and help come from its metadata

  deploy {:doc "Manage deployments"
          :cli {:epilog "See "}  ; parser options for this task
          :cmd {"lock"   {:exec-fn deploy/lock}             ; a command tree
                "unlock" {:exec-fn deploy/unlock
                          :spec   {:force {:coerce :boolean}}   ; leaves are plain
                          :epilog "Releases the lock."}}}}}     ; babashka.cli nodes

๐Ÿคฉ 1
๐ŸŽ‰ 4

For folks on Windows, the bash script to get the dev build also works in git bash

Gonna try it now btw, not sure if this is a good idea and if it works in all settings, but it seems like a handy trick to try out existing bb wrapper scripts with the tmp version:

PATH=/tmp/bb-snapshot-dir:$PATH bb -version
babashka v1.12.219-SNAPSHOT

I can easily verify this way the :repl option works again before and after

๐Ÿ‘ 1

(otherwise I have to reinstall the wrapper script with bbin)

wrapper script?

yeah so the script that is using the repl is a babashka script installed with bbin. So it uses the bb binary on the path. Changing the PATH env allows me to test this script with the new bb binary without touching the script itself. I dont know maybe it is obvious but i didn't think of this before

fyi I can't find the test text anymore from https://github.com/babashka/babashka/tree/cli-tree-tasks/test-cli Do you still want feedback on that?

I removed that prose since that was just a working document. What counts is the implementation. When I'll release I'll write a blog post. But some testing would be nice

๐Ÿ‘ 1

Maybe my bad, but I remember the working document had some instructions on how to setup the completions. I have trouble finding these instructions (or similar) now

ah sorry: source <(bb org.babashka.cli/completions snippet --shell zsh)

๐Ÿ™ 1

deploy + space + TAB should complete, if not, it's a bug

Yeah that does not seem to complete

@jeroenvandijk

borkdude@MBP25-3 /tmp/bb-cli-test $ source <(bb org.babashka.cli/completions snippet --shell zsh)
borkdude@MBP25-3 /tmp/bb-cli-test $ bb deploy
lock    -- Take the deploy lock
unlock  -- Release the deploy lock

which bb
/Users/borkdude/dev/babashka/bb

Seems to work fine here

which shell are you using? zsh?

The deploy<TAB> just adds a space (so no completion)

โžœ  babashka-test which bb
/tmp/bb-snapshot-dir/bb
โžœ  babashka-test bb -version
babashka v1.12.219-SNAPSHOT

what does your bb.edn look like exactly?

{:tasks
 {:cli   script.cli/defaults        ; parser options for every CLI task

  build  {:doc  "Build it"          ; a plain task: untouched by any of this
          :task (println "building")}

  ; dev    {:exec-fn dev/run}         ; a handler: options and help come from its metadata

  deploy {:doc "Manage deployments"
          :cli {:epilog "See "}  ; parser options for this task
          :cmd {"lock"   {:exec-fn deploy/lock}             ; a command tree
                "unlock" {:exec-fn deploy/unlock
                          :spec   {:force {:coerce :boolean}}   ; leaves are plain
                          :epilog "Releases the lock."}}}}}

is it possible to put your project in a github repo so I can test locally?

I disabled dev because I don't have the ns configured

it is not much

git clone 

maybe something else in my zsh is breaking things. Should i have a specific version?

borkdude@MBP25-3 /tmp $ git clone 
Cloning into 'bb-cli-test-case'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (3/3), done.
borkdude@MBP25-3 /tmp $ cd bb-cli-test
borkdude@MBP25-3 /tmp/bb-cli-test $ bb deploy
lock    -- Take the deploy lock
unlock  -- Release the deploy lock

do you get autocompletions for a normal exec-fn with bb.cli specs?

if not, then maybe your old bb tasks completions are still active

i didn't clean up anything. Let me check if have something old lingering around

> do you get autocompletions for a normal exec-fn with bb.cli specs? Not sure. Do you have an example i can test?

just :tasks {lock {:exec-fn deploy/lock}}

it should list the --env option when doing bb lock<TAB>

No, that doesn't work either

what actually does work?

the completion of the task and the display of tasks (the menu)

that is new, right?

the completion of the tasks already existed if you had the snippet from the book installed

Ok i didn't have installed

what version of zsh do you have? I'm not sure why everything this new feature is about doesn't work :)

zsh --version
zsh 5.9 (arm64-apple-darwin25.0)

same here but on darwin 24

what terminal are you using

can we try once more? open a fresh iterm2. then add your tmp bb to the PATH. then which bb then eval "$(bb org.babashka.cli/completions snippet --shell zsh)" and then bb <TAB>

and then bb lock <TAB>

@lee also tested zsh on linux, it all worked for him

also make sure you are using the latest dev build

and check the SHA in bb describe

that looks good

bb describe
{:babashka/version   "1.12.219-SNAPSHOT"
 :git/sha            "42d4fd0f151ec2c0a78bc60fcd6d04312578e6e4"
 :feature/csv        true
 :feature/java-nio   true
 :feature/java-time  true
 :feature/xml        true
 :feature/yaml       true
 :feature/jdbc       false
 :feature/postgresql false
 :feature/sqlite false
 :feature/hsqldb     false
 :feature/oracledb   false
 :feature/httpkit-client true
 :feature/lanterna false
 :feature/core-match true
 :feature/hiccup     true
 :feature/test-check true
 :feature/spec-alpha false
 :feature/selmer true
 :feature/logging true
 :feature/priority-map true}

Is there a way to find other potential interference of completions? I've checked the zshrc files and I don't see something suspicious, but I did mess with these things in the past. So maybe i'm missing some directory

hmm I don't get option auto-completes anymore for bb dev.. darnit. I'll debug

this still works though:

$ bb deploy
lock    -- Take the deploy lock
unlock  -- Release the deploy lock

does this work?

$ bb dev --help
Usage: bb dev [options]

Run the dev system

Options:
      --port  HTTP port (default: 8080)
  -h, --help  Show this help

Reads config from dev.edn.

bb dev --help
----- Error --------------------------------------------------------------------
Type:     java.lang.Exception
Message:  File does not exist: dev

wait i disabled that task

bb dev --help
Task dev: cannot resolve :exec-fn dev/run: Could not locate , dev.clj or dev.cljc on classpath.

dude, this is the git repo I just cloned right?

oooh I was in /tmp/bb-test-cli instead of /tmp/bb-cli-test-case facepalm

ok:

$ bb TAB
build   -- Build it
deploy  -- Manage deployments

and no completions for bb deploy TAB

but of course that is because there is no deploy.clj file

just chiming in from a Linux 7.1.4 fedora machine, all seems to work. at least the subcommands. yet to test out the spec

> but of course that is because there is no deploy.clj file Should it give an error on completion when there is an unexisting reference (like deploy) in bb.edn ?

@jeroenvandijk add this to your bb.edn:

:paths ["bb"]
and this file to bb/deploy.clj
(ns deploy)

(defn lock
  "Lock env"
  [_])

(defn unlock
  "Unlock env"
  [_])

๐Ÿ‘ 1

I don't think completions should give error messages

no completion after bb deploy I've pushed to the repo

This is not good I think

bb deploy lock
:tasks :cli script.cli/defaults cannot be resolved: Could not locate script/cli.bb, script/cli.clj or script/cli.cljc on classpath.

remove that line

And option autocompletion also works now

bb deploy unlock--force

and --help?

Maybe for debugging it would be handy to have some error output during completion ๐Ÿ˜…

and bb deploy lock --help?

(`--help` was also completing on - )

yeah some debug info would be nice, but completions don't work with debug info I think... I'll take a look

Or maybe something like bb simulate-completions and then give error output or something I don't know. You have probably better ideas about this haha

I'll see what I can do

๐Ÿ‘ 1

But the feature looks great! Can imagine it being very useful

based off what I see in the nushell completer, could you do something like:

๎Š… ../bb org.babashka.cli/completions complete --shell nushell -- "deploy" "lock" ""
dev
prod
staging
(switching out the shell name) to get part of the way to 'simulate-completions', in order to isolate some variables

๐Ÿ‘ 1

that's a great way to debug

I now published the blog post here: https://blog.michielborkent.nl/babashka-tasks-cli.html and the new release is building If you see anything weird in the blog post, lemme know

cc @lee

๐Ÿ‘€ 1

Looks good! I see you added a debug option nice. one error at end The task integration is available in babashka 1.13.219. > should be 1.12.219 i guess?

no, 1.13 is coming up

it has the new 1.13 keys! stuff so I thought I'd call it that ;)

Very nice! DMed you a couple of very minor tweaks!

๐Ÿ™ 1

ok, fully available now

2

thanks for the testing!

and proof-reading