no amount of ide smartness or agentic shenanigans is going to replace the feeling of having development process in sync with your thought process
https://clojure.org/news/2020/02/20/state-of-clojure-2020
The most recent report:
One thing I built: defun https://github.com/killme2008/defun -- a macro for defining Clojure functions with pattern matching, Elixir-style. Still probably my favorite thing I've open sourced.
I had an idea about writing something similar, but for multimethods, but never got around thinking it through and trying it out.
The way defmulti and defmethod work is that they do a concurrency safe operation on a data structure, which is used to dispatch to the right method when you call the function.
My hunch is that it should be possible to do something similar by using core match. What I don't know is whether it's a good idea or a terrible one though. When you're already doing pattern matching, then you likely want to see everything in one place like with your library.
Something where I feel Kotlin did better.
For me the best way to introduce something like this is that I can actually start with small software increments on a Spring Java project.
The syntax is the best in the world (how computer's really operate?) but it's always been a pain to setup the tooling for me. I'm dumb like that. Now with AI it's become super easy to get back into the REPL and I'm in heaven.
Totally moving it back into workflow and proposing to bring it back into the dayjob.
and the benefit of lisp comes from the fact that every expression is fully delineated by parentheses, so you don't need to select any text, find the start or the end of any syntactic construction, you just eval the form you're standing at and see the result
or just as easily you can wrap that form, bind the locals to test values and eval that to see what changes
An example of me solving an Advent of Code with clojure and repl. You can see i never interact with the repl directly, I just send code to it via my editor and get results inline.
Not even close.
1. REPL is automatically compiled into running systems 2. Great hot-reloading support
So it's generally very easy to "poke" at a running system, and the whole dev process assumes you will do this.
TBH, these days it is largely possible in a C++ debugger. Less so 10 years ago, though.
I don't know exactly what you mean by this, but Clojure syntax is not really anywhere close to how computers actually process instructions.
Clojure is very nice though.
there's a detailed explanation here: https://youtu.be/Djsg33AN7CU?t=659
We have a codebase at work that was "stuck." We've consistently done minor library upgrades, but no major upgrades in several years, and was recognized as a major piece of technical debt / minor disaster for almost two years, in that we urgently needed to dedicate an engineer to it for a month or more to bring it up to date. We also suspected that framework upgrades would improve performance enough to save us a little bit in operating costs. I got curious, created a branch, and threw Claude at it. Claude knocked it out in a couple of days while I mostly worked on other things. Then we dedicated several engineer days to doing extra manual testing. Done and deployed. Now we're ready to experiment with giving it less resources to see if the performance improvement holds up in practice.
This codebase was only about 200k lines of code, so probably smaller than yours. Really curious how it would go with a larger codebase.
EDIT: Claude may only have taken a couple of days because I was only checking in occasionally to give it further instructions. I don't know how fast it would have been with my complete attention.
All that said, 90% of the time you still just eval a bit of a code to see what happens and that's the same between the two languages.
The cycle is:
1. Write production code.
2. Write some dummy code in the same file (fake data, setup).
3. Evaluate that dummy code. See what happens.
4. Modify code until satisfied.
Your feedback loop is now single digit seconds, without context switching. It's extremely relaxing compared to the alternatives (rerunning tests, launching the program with flags, what have you).I love the idea of an AI integrated repl (like what Jeremy Howard and team have done with solveit), it's far more in line with my preferred vision of the AI augmented future of coding. Less "swarm of agent" more, "learn with the agent".
(you can theoretically pass "reload": true (or similar option) in launch.json for auto reload, tho I haven't felt the need to use that in my workflows.)
"Typical REPL" workflow: Have one editor open, have one REPL open, have one terminal open that runs the application. One change is typically: Experiment in the REPL window, copy-paste into your editor, write tests, restart application (lose all state), setup reproduction state, test change. Or something like this.
In a Clojure REPL workflow, you'd do something like: Have one editor open, this starts the REPL and often the application in the background too. One change is typically: Edit code, evaluate that snippet of code (which sends it to the REPL and the running application), write tests, evaluate them too in the editor, if you're happy, hit CTRL+S and you're done. Application still has the existing state, no restarts needed and you essentially never have to leave the editor window/pane.
Of course, others might have slightly different workflows, but for myself and many (most?) other Clojure developers I've observed in the wild, this is pretty much the standard.
Personally, I find waiting more than 200ms unacceptable and really < 50ms is ideal. When the feedback is very small, it becomes practical to save the file on every keystroke and get nearly instantaneous results with every input char.
Feel like a holy grail to me to back in the hot seat with repl driven but I can drop in and figure things out... all on the JVM. Madness!
Setup is:
- nvim --listen /tmp/nvim — starts Neovim with a socket Claude can connect to
- /mcp in Claude Code — enables the Neovim MCP server, gives Claude direct control of Neovim
- lein repl in the nvim terminal
- Claude reads .nrepl-port, runs :ConjureConnect — REPL is live!
The loop is so dope:
- Claude writes code directly into my .clj files
- Then evals it into the running process via Conjure
- Sees the result in the REPL, iterates if wrong, all in the same conversation turn
- wrap-reload middleware means the web server hot-reloads changed namespaces on the next request
I recently had to design a new reference data system for the manufacturing domain. During the initial design stages we brought up the idea of using the Clojure programming language. Even if at first I was skeptical on deviating from the standard development stack, I gradually realized the value of taking advantage of the multpile opportunities offered by this language.
This blog post is a high level take on the use of the Clojure programming language and its introduction into my development stack. I will present here the points that guided the decision to use it over more conventional languages like Java.
Clojure is a dynamic functional programming language that runs on the Java Virtual Machine and which is a member of the Lisp family ( yes you heard it right, the very old one )

As a result, Clojure not only benefits all the well-known advantages of functional programming languages such as immutable data structures ( which I will not describe in this article ) but it also provides all the advantages related to its Lisp nature, such as code-as-data semantic.
In addition, the Clojure ecosystem provides a wide range of powerfull libraries and tools for manipulating data which makes it very convenient for prototyping complex data validation and transformation programs.
Clojure language is not a newcomer, it was created in 2007, but has been considered for many years as a "hobbyist" language used in very specific cases only, although it was intially designed with the objective of being multi-purpose and stable for business usage.
However reports tend to show a significant increase in the use of Clojure in enterprise in the recent years. I can mention the fact ThoughtWorks radar passed Clojure language to "adopted" since 2014, meaning Clojure position is an enterprise ready technology for quite a long time.

A first element of decision lied in the fact that our product used a great number of data structures and business management rules which aim to evolve very frequently and be adapted to new business contexts over time.
So the idea came to use text descriptors a.k.a DSL ( Domain Specific Language ) to specify business logic instead of "hard-coded" pieces of code.
Thereby the use of conventional object oriented programming languages did not seem appropriate, while Clojure provided all the necessary features by design or using powerful libraries such as malli or specter.
On the other hand, I had to evaluate the complexity of some business-cases and demonstrate feasibility in the early stages of development. The Clojure-toolbox provided ways to ease data exploration, and allowed us to develop prototypes in a very short time while the use of alternative languages would have required a lot of boilerplate code.
The very first advantage of Clojure was bought by its nature of Lisp. Clojure uses a superset of EDN (Extensible Notation Format) which allows code declaration in a readfullness notation format.
(defn person-name
[person]
{:pre [(s/valid? :acct/person person)]
:post [(s/valid? string? %)]}
(str (:acct/first-name person) " " (:acct/last-name person)))
Thus, Clojure is well suited to creating rich DSLs that integrate seamlessly into the language. You may have heard Lisps boasting about code being data and data being code.
Many of the features of Lisp have made it into other languages, but Lisp's approach to code-as-data and its macro system still set it apart.
The software product I was working on required to implement a lighweight language to express business logic in a declarative way, like a rules-engine. This use-case has been achieved using a single Clojure variable.
(def business-logic
[:ruleset-group
[:ruleset1
[:when [:condition1][:condition2]]
[:then [:and [:action1][:action2][:action3]]]
[:ruleset2
[:when [:condition3] [:or [:condition4][:condition5]]]
[:then [:and [:action4][:action5][:action6]]]]]])
The ambition here was just to use a simple and concise data structures that can be easilly modified by non-developers and persisted in database. But Clojure provides features to implement way more complex DSLs using its macros system.
Another key feature that made me go for Clojure is its REPL environment (standing for Read-Eval-Print Loop) which enables the programmer to interact with a running program and modify it, by evaluating one code expression at a time.
REPL is a secret weapon for Lisp programmers and it is a tool that enables you to interactively contact with the program you write and evolve it rapidly. Discourses of REPL Driven Development never end in Clojure community and it has become quite popular. The reason for this is that you can productively and quickly write your code. The progresses such as finding a bug or adding/removing a feature can be easily carried out through quick feedbacks.

The ability to develop and test quickly in an iterative approach helped a lot during the initial exploration phase. Developers had the possibility to instantly experiment new code snippets during workshop sessions, and therefore to incredibly increase efficiency. As the Clojure docs says very well :
Many Clojure programmers consider the REPL, and the tight feedback loop it provides, to be the most compelling reason to use Clojure. This does not mean that the language features of Clojure, such as immutable data structures, are not valuable: the Clojure REPL gets most of its leverage because of these features, in particular because Clojure was designed with interactive development in mind.
In addition, the integration of REPL in IDEs plugins such as cursive or calva pushes this interactivity a step further, and allows on-demand execution of small portions of code or even single expressions.
As it was written on the JVM, Clojure gives access to all available Java libraries and their frameworks. So you can call Java code from Clojure code, or vice versa.

In my context this was a huge advantage, as it helped to integrate seamlessly Clojure with our standard development stack: Java/SpringBoot.
Nevertheless, despite the fact that the interop feature reduced the difficulty for us to on-board the Clojure world, a crucial decision to be made was the depth of use.
Several options were put on the table, knowing that the more complex features were used, the more the required level of experience grew. The challenge for me was therefore to find a good balance between short-term productivity, and mid-term risks around the management of the skill matrix of our development team.

I finally decided to learn by walking, by first limiting use-cases to prototyping purposes, and then gradually integrating functions and modules into our product code.
In this way, we added with success several features directly taking advantage of code-as-data sematic and REPL, while limiting the overall complexity of our technical solution. Moreover this allowed the dev team to improve their knowledge of the Clojure programming language over time.
See below a list of extra features that I considered for the future steps.
Macro system: allows the compiler to be extended by user code. Macros can be used to define syntactic constructs which would require primitives or built-in support in other languages. It fits well our custom DSL engine writing challenge.
ClojureScript: ClojureScript is a compiler for clojure that targets JavaScript. It generates code that can be executed on browser side. Thus, it allows to reduce the technological frontier between frontend and backend
Data-friendly Libraries: Clojure ecosystem provides a wide range of libraires such as ClojureSpec, Schema or Malli that would be interesting in our context.
When deciding how deep to go with Clojure, it is important to take staffing into consideration. Clojure's functional and data-oriented approach is antagonistic to a lot of more traditional programming wisdom. For teams with experience in object-oriented development only, the learning curve can be steep. The number of experienced Clojure developers on the market also is relatively small.
It will therefore be better to consider a very progressive rampup on this technology in order to gradually leverage the nice features listed above, one after another.
The man who removes a mountain begins by carrying away small stones.
In my personal case, this method turned out to be worth-it because it allowed my team to be effective from the very first moments of project lifecycle, and then to deepen technical use cases as they went along.
To date, I'm constantly re-evaluating the opportunities for exploiting Clojure in other parts of my software architecture, while taking care to improve and maintain a high level of competence in my development team.
To summarize, here is a synthesis of arguments that made Clojure a go-to option.
That being said, one may serously considers to give it a try for new projects requiring rapid data-driven development.
* Clojure is built different in terms of hot code reloading
* the REPL is its own application process in languages Ruby or Python, but in Clojure it’s sortof a client for the system
Is that right? Is there more to it?
The fun way to get a feel for lisp machines is emacs, it's so easy to fall of a language and especially hand-coding in a language if you don't have to.
So in a JavaScript server, you might have the database connection set in some config/thing you pass around to request handlers to use, and if you change a request handler, you typically stop the entire application, then start it again. Then the database connection (and the entire config in fact) would need to again connect.
In a Clojure application, first you either start the repl from the server, or start the server from the repl, then you might instead keep the database connection in an "atom" that you keep around for as long as you have the repl/server running. And instead of restarting the process when you change a handler, you'd only change the handler.
And yeah, the libraries and ecosystem at large is built with this (mostly) in mind for everything. The language also supports this by letting you "redefine" basically anything in a running Clojure application, which would be harder in other languages.
I've done some experiments years ago for doing the same in JavaScript, and it kind of works, but every library/framework in the ecosystem is not built with this in mind, so you'll be writing a lot of your own code. Which, now with LLMs, maybe is feasible, but can't say it was at the time exactly.