clojure-lsp https://clojure-lsp.io/ 2026.07.06-14.34.19 released with a goal for this release: performance improvements! ✨ • This release I decided to focus efforts on performance, tracking all progress in https://github.com/clojure-lsp/clojure-lsp/issues/2313, lots of details there, this affects all clojure projects, making clojure-lsp starts faster and use less mem and cpu overal, here's TLDR: ◦ Warm startup ~40% to 83% improvement: metabase went from 84s -> 14s ◦ Cold startup cpu: ~33 to 39% improvement ◦ Memory reduced ~51 to 60%: metbase went from 868mb -> 439mb • Lots of bumps, including automating clj-kondo bump when master commits lands. • Fix some cases where LSP cache write can goes wrong and project is never cached, (like metabase) • We now have performance tests to make sure clojure-lsp is not having regression in performance - Kudos @john.t.richardson.dev for the help! • We allow rename unqualified keywords now. https://github.com/clojure-lsp/clojure-lsp/issues/2139 Changelog in the 🧵 Happy coding!
• add missing namespace form, guessing at the name if outside of project sources,
when adding a missing :require or :import via the Add Require code action.https://github.com/clojure-lsp/clojure-lsp/issues/1734
• Reduce memory usage of java class and member definitions analysis.https://github.com/clojure-lsp/clojure-lsp/issues/2314
• Shrink db cache file considerably not serializing redundant analysis elements uri.https://github.com/clojure-lsp/clojure-lsp/issues/2315
• Run the db cache write on a dedicated thread so the blocking write no longer ties up a core.async dispatch thread during startup.https://github.com/clojure-lsp/clojure-lsp/issues/2318
• Skip re-analysis of unchanged source paths on warm startup by persisting the internal analysis, dep-graph, documents and clj-kondo findings in the db cache and only re-analyzing source files whose checksum changed.https://github.com/clojure-lsp/clojure-lsp/issues/2316
• Optimize clj-kondo analysis ingestion with single-pass normalization using transients, a process-global filename->uri cache, and shallow batch merging.https://github.com/clojure-lsp/clojure-lsp/issues/2317
• Scale the JVM server heap to a percentage of available RAM (`-XX:MaxRAMPercentage`) instead of a fixed`-Xmx`, matching the native image and avoiding out-of-memory on very large projects.https://github.com/clojure-lsp/clojure-lsp/issues/2313
• Analyze external java member definitions lazily on first navigation/hover/completion instead of all up front, drastically reducing memory usage on projects with large dependency sets. Set`:analysis :java :member-definitions`to`true`to keep the previous eager behavior.https://github.com/clojure-lsp/clojure-lsp/issues/2313
• Publish startup diagnostics directly and off the`initialize`critical path, so large projects become interactive much sooner (warm`initialize`dropped from 73s to 8s on a large monorepo, with diagnostics streaming in right after).https://github.com/clojure-lsp/clojure-lsp/issues/2326
• Sanitize clj-kondo findings before caching, custom hooks can attach non-serializable data to findings breaking the cache write, and write the db cache atomically so a failed write never leaves a truncated cache behind.https://github.com/clojure-lsp/clojure-lsp/issues/2313
• Added Performance integration tests for server initialization: Measuring Cold Start and Warm Start, ensuring that future changes don't regress the startup time of the LSP server.
• Fix crash when using`:exclude-when-defined-by`as a vector and not a set.https://github.com/clojure-lsp/clojure-lsp/issues/2292
• Fix`cyclic-dependencies`linter falsely reporting cycles for`:as-alias`requires.https://github.com/clojure-lsp/clojure-lsp/issues/2108
• when sorting or removing :require or :import namespaces during ns organization, group comments and clj-kondo directives along with themhttps://github.com/clojure-lsp/clojure-lsp/issues/1237
• Auto generate clojure-lsp nightly builds given kondo master commits (nightlies).
• remove restriction on renaming unqualified keywordshttps://github.com/clojure-lsp/clojure-lsp/issues/2139
• Bumps:
clojure: 1.12.4 -> 1.12.5
core.async: 1.8.741 -> 1.9.865
clj-kondo:`2026.05.26-20260703.222101-23`
rewrite-clj:`1.2.55`
cljfmt: 0.16.0 -> 0.16.4
sci: 0.12.51 -> 0.13.52
rewrite-edn: 0.4.9 -> 0.5.9
transit-clj: 1.0.333 -> 1.1.357
tools.namespace: 1.5.0 -> 1.5.1
babashka/cli: 0.8.67 -> 0.11.72
matcher-combinators: 3.9.2 -> 3.10.0
promesa: 11.0.678 -> 12.0.0
opentelemetry: 1.59.0 -> 1.63.0
nrepl: 1.5.2 -> 1.7.0
cider-nrepl: 0.58.0 -> 0.59.0
clj-async-profiler: 1.6.2 -> 1.7.0
deps-deploy: 0.2.2 -> 0.2.5
graal-build-time: 1.0.5 -> 1.0.6
wow, on my largeish project clojure-lsp diagnostics + a warm cache went from ~35 seconds to 6 seconds 🚀
that's great! Interested in knowing what other projects were improved from people!
@ericdallo how can I check the timings?
before:
2026-07-06T16:44:55.328Z INFO [clojure-lsp.handlers:170] - :lsp/initialize 156913ms
now:
2026-07-06T16:50:21.643Z INFO [clojure-lsp.handlers:170] - :lsp/initialize 78525ms
🏃 🏃 🏃I see a ton of java.lang.NoSuchFieldErrors though:
2026-07-06T16:49:20.409Z WARN [clojure-lsp.kondo:495] - [clj-kondo] Error parsing java file /Users/felipecortez/.m2/repository/org/clj-commons/dirigiste/1.0.4/dirigiste-1.0.4.jar:io/aleph/dirigiste/Stats.java with error #error {
:cause arguments
:via
[{:type java.lang.NoSuchFieldError
:message arguments
:at [com.github.javaparser.metamodel.PropertyMetaModel getValue PropertyMetaModel.java 263]}]
:trace
[[com.github.javaparser.metamodel.PropertyMetaModel getValue PropertyMetaModel.java 263]
[clj_kondo.impl.analysis.java$node__GT_member$iter__18870__18874$fn__18875 invoke java.clj 208]
those weren't in the previous lsp server logs I savedHum interested, it might be related with javaparser from clj-kondo c/c @borkdude
Startup went from 94s -> 38s for us! Gonna make a big difference, especially with LLMs using clojure-lsp. We frequently OOM our machines.
@ericdallo Just merged another ~10% perf/alloc improvement in clj-kondo (via a simple measure: map -> record)
When I rename a namespace in VSCode, I can get into a state where the filename is renamed, but the namespace isn't. This appears to happen because there's a back-and-forth protocol where clojure-lsp requests the filename is changed, and then, on notification that it was changed, sends a request to change the namespace text. (more in thread) 🧵
The issue is that VSCode allows the user to cancel the namespace rename (even without my "needs confirmation" changes). When cancelled, the filename is changed but the namespace change is cancelled, leaving the filename inconsistent with the namespace. On investigation, it seems that this is a brittleness in LSP's protocol. A workaround seems to be to send both the filename change and the namespace change together as a grouping, and then return nothing when LSP is notified that the changes had occurred (willRenameFiles). The problem is that this requires some sort of indication that nothing should be done, because if the user renamed the file (instead of the namespace), the namespace change would still need to be sent. I couldn't find anything about this in the issues or here on slack, but this happens to me kinda often. Is it known?
yeah, this already gave so many headaches in emacs,vscode etc, it seems each client acts in a different way
the thing is, I already did lots of changes to this to "work" in all editors, but won't be surprised if something is still not working after years
and it's pretty complex, we change this behavior and we break other editor possibily, we would need to test on emacs, vscode and intellij to make sure it's correct
Ah, OK. So I won't mess with it for now. And maybe I'll hope that LSP comes up with a better protocol, since they are allowing clients to reject changes now.