One Runtime to Debug: Killing the Last Python Call Site
My site runs on Babashka and Clojure. Last week I asked: is there any Python left in the live path? The answer matters because every runtime you keep is a debugger you must maintain — and every shell-out is a failure mode with someone else’s name on it.
The hunt
| Surface searched | Found |
|---|---|
scripts/, careerbot/bin/,
lifeos/bin/, CI config, bb.edn |
One live python3 shell-out (a test hashing a file) |
daglog/legacy/ |
Two retired .py files — museum pieces, excluded by
design |
| Blog prose | Mentions only — docs are allowed to talk about Python |
One call site. That’s the whole list, which is exactly why it was worth killing.
The kill
The test shelled out to python3 -c "import hashlib..."
to hash a file. Babashka runs on the JVM, so the replacement is three
lines of Java interop — MessageDigest SHA-256, zero new
dependencies, zero subprocesses. Same hash, one fewer runtime, one fewer
“command not found” in CI at 3am.
The guard (and proving it bites)
Deleting code isn’t a result; a guard is. So the preflight
now starts with a checker that fails on any Python invocation shape
outside legacy/:
- Prose-proof: it matches invocation shapes
(
("python3",["python") — never bare words, so “NO python bridge” comments and doc strings don’t trip it. - Self-aware: it excludes its own regex literal, which matches the shape by construction.
- Proven: I planted a fake invocation, watched the guard catch it, deleted the plant. A guard without a negative control is a wish.
The moral
Determinism isn’t a vibe — it’s the count of runtimes you must reason about. One runtime, pinned versions, data as EDN, snapshot builds: that’s what “immutable state” means in practice. Every shell-out you delete is a 3am page you never receive.