Bringing Clojure programming to Enterprise (2021)
blogit.michelin.io
blogit.michelin.io
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.
I find myself missing Clojure-style multimethods in most languages that aren't Clojure (or Erlang); once multimethods clicked for me, it seemed so blatantly obvious to me that it's the "correct" way to do modular programming that I get a little annoyed at languages that don't support it. And core.async is simply wonderful. There are lots of great concurrency libraries in lots of languages that give you CSP-style semantics (e.g. Tokio in Rust) but none of them have felt quite as natural to me as core.async.
I haven't had a chance to touch Clojure in serious capacity in awhile, though I have been tempted to see if I can get Claude to generate decent bindings for Vert.x to port over a personal project I've been doing in Java.
I remember my initial confusion, but it didn't take long when I suddenly felt flabbergasted - shit just made sense. It was so down-to-earth, inexplicably pragmatic and reasonable that it made me want to learn more. I didn't even build anything with it, I was just browsing through related Google search results when I saw "Clojure/Remote Conf" announcement. It was a no-brainer - I took a day off and joined from my home computer. I immediately became a fan-boy. The amount of crazy awesome stuff I saw, the people I met in the chats, the articles and books I put in my notes - all that made me feel excited. After the conference I sat in my chair staring at the blank screen, for 40 minutes or so. Thinking, meditating, contemplating if that was a mid-career crisis or something. Knowing that on Monday I would have to go back to the same struggle, same shit, same mess that I had for the past two years, everything that until this very point made me feel depressed. On Monday I went back to work and said I'm leaving because: "I saw things I cannot unsee". I just knew I could never sneak-in some Clojure there. So I left. Even though it was well-paid job, fifteen minutes away from my home.
Getting into Clojure radically re-opened my eyes to the entire concept of computing. Not only had I found a different way of programming - I felt so enlightened, and largely thanks to the people I met in the community, which deserves special acknowledgment. Clojurians are just made different - they are the kindest, extremely knowledgeable, tolerant and most sincere professionals I have ever met. Not a single time when I asked them a question - no matter how dumb, provocative, or confusing it was; they always, every single time gave me much deeper and thought-provoking answers than I ever expected. None of my inquiries were ever dismissed, ignored or rejected. They'd gladly discuss just about anything - no matter the language, tool, technique, or ideas. Whatever helps you to find answers or get closer to the solution. I know, I have become a better programmer, thanks to Clojure. Yet more importantly, it helped me to become a better person.
Yes, I regret stumbling on Clojure. I wish I never saw it when I wasn't ready for it. It makes me feel sad for the time I have wasted. I wish I had someone persuasive to convince me to learn it sooner.
- syntax is hard to read unless you spend a lot time getting used to it
- convention for short var names makes it even harder
- function definition order makes it even harder
- too dynamic for most people's taste
- no type safety
- the opposite of boring
- no clear use case to show it clearly beating other languages
- niche with small community and job market
- JVM
For all those reasons its a hard sell for most imo.
it's not even in the top 50 here: https://www.tiobe.com/tiobe-index/. Lisp is 26.
Besides, the community and ecosystem is large enough that there are multiple online spaces for you to get help, and personally I've been a "professional" (employed + freelancing) Clojure/Script developer for close to 7 years now, never had any issues finding new gigs or positions, also never had issues hiring for Clojure projects either.
Sometimes "big enough" is just that, big enough :)
Don't worry, I don't think anyone took your comments as "attacking Clojure", I certainly didn't :)
Every problem people face is "not a problem" or "actually a good thing" or, maybe if all else fails we can make users feel bad about themselves. Clojure is intended for "well experienced, very smart developers". Don't you know, our community skews towards very senior developers! So if you don't like something, maybe the problem is just that you're not well experienced enough? Or, maybe what you work on is just too low-brow for our very smart community!
How about just "different"? Turtle want to teach everyone to program, that's fine, just another way of building and maintaining a language. Clojure is clearly not trying to cater to the "beginner programmer" crowd, and while you might see it as "unhealthy attitude", I'd personally much prefer to realize having many different languages for different people is way better than every language trying to do the same thing for the same people. Diversity in languages is a benefit in my eyes, rather than a bad thing.
For some reason I doubt this is in any way representative of the real world. Scratch, which is a teaching language for children, bigger than PHP? Which is smaller than Rust? Yeah, these are results you get when you look at the Internet, alright.
Let's be honest and avoid painting a misleading picture. Getting a job as a software developer of any kind is genuinely difficult right now. Finding a position on a Clojure team has always been relatively harder for various reasons - and not simply because of its [in]popularity.
Clojure tends to attract older, more experienced developers. If you want a full-time Clojure role but have no prior experience with it, you'll often need to accept a junior-level salary - something many seasoned developers can't afford or simply won't do.
Junior developers have it even harder. Recruiting pipelines don't really distinguish between experience levels - everyone goes through roughly the same process, and juniors are expected to keep up with veterans, with almost no room for error.
Senior, battle-tested Clojure devs face a different kind of pressure. Interviews are frequently grueling, mentally exhausting sessions comparable to architect-level evaluations in other places. And because Clojure enables small, skilled teams to accomplish a lot, companies rarely need to hire in bulk - so competition for each opening is fierce.
This creates a frustrating situation for everyone, companies included. They want top-tier talent but offer junior salaries, while simultaneously rejecting juniors and anyone without direct Clojure experience. Supply and demand are badly out of balance.
That breeds resentment - "why bother learning it if I'll never get hired?" Honestly, there's no clean answer, and Rust seems to be in a similar spot right now. Even so, the language is worth learning. It has real practical value, even when you're not using it on a team. The future-proof choice I believe is to learn both - Rust and Clojure. Exploring both of these languages, I can honestly think things will change in their favor. Unless you want to stay sad at nearly-burnout levels for the next decade or more with TS/Python/Java/etc.
If we've measured the value of jobs by their popularity, everyone would want to be a retail salesperson or a cashier - according to US Bureau of Labor Statistics data, these are most common occupations in the States. People still writing Clojure professionally after 15 years (of other languages) are disproportionately serious engineers. The language self-selects. Small community means concentrated competence, not weakness. The network effect assumption is wrong here - a programming language isn't a social network. A better hammer isn't worse because fewer people own one. Most job listings reflect what organizations already know how to hire for, not what produces the most value.
That is, prose is good for entertainment, but less so for conveying information, even less so for exactness.
Not to mention in a post AI world, cost of code generation is cheap, so orgs even need even fewer devs, combine all this with commonly used languages and frameworks and you need not worry about - "too valuable to replace or fire".
Having said that - there may be a (very) small percentage of orgs which care about people, code crafting and quality and may look at Clojure as a good option.
> understood, i'm just pointing out that people seem to prefer the apple over the orange.
This is kind of like saying that fewer people are drinking Coke every year and are choosing other beverages. It might be objectively true but it glosses over the fact that literally billions of people drink Coke daily and will continue to do so for decades to come.
The JVM is the same. Some people and organizations might be using zig or mojo (and I have absolutely nothing against zig or mojo, to be clear, I hope they succeed) but many multiple orders of magnitude more individuals and organizations run JVM stuff in a given year and will continue doing so.
At this point, the JVM is a civilizational technology. If it went away tomorrow, multiple banks would fail, entire countries would no longer be able to administer social services, millions of people would die. The JVM is in everything.
Developers on HN using zig, mojo, etc. aren't really a representative sample.
Agree. A lot of manipulation and astroturfing goes on, from many different groups, that affects what appears to be popular on a particular site. The bubbles formed at a site can become self-reinforcing.
The more time one spends at a particular site, the more likely to fall for the illusion or become to believe that what is being presented, is representative of everyone or everywhere else. Kind of like if a person only watches Fox News or PBS News.
I have to push back on this one, respectfully.
Clojure is easily the most boring, stable language ecosystem I’ve used. The core team is obsessed with the stability of the language, often to the detriment of other language values.
This attitude also exists among library authors to a significant degree. There is a lot of old Clojure code out there that just runs, with no tweaks needed regardless of language version.
Also, you have access to tons of battle tested Java libraries, and the JVM itself is super stable now.
I won’t comment on or argue with your other points, but Clojure has been stable and boring for more than a decade now, in my experience.
There’s a rule of thumb: write a macro as a last resort.
It’s not hard to stick to it. In general, you can go a long, long way with HOFs, transducers, and standard macros before a hand-rolled macro would serve you better.
Have you worked for a company that hasn’t created its own, as you put it “mini language”?
Have you worked for a company that doesn’t indulge in over engineering, over abstraction and hidden cost?
Do you actually do programming for a job at all?
because programmers suck we should make tools that make it easier for them to suck?
wouldn't want to work at a place with those kind of values
Creating these mini DSLs is something that requires a lot of thought and good design. There is a danger here as you pointed out sharply.
But I have some caveats and counter examples:
I would say the danger is greater when using macros and far less dangerous when using data DSLs. The Clojure community has been moving towards the latter since a while.
There are some _very good_ examples of (data-) DSLs provided by libraries, such as hiccup (and derived libraries), reitit, malli, honeysql, core match, spec and the datalog flavor of Clojure come to mind immediately (there are more that I forget).
In many cases they can even improve performance, because they can optimize what you put into them behind the scenes.
That’s pretty much exactly the opposite of how I always felt. Perhaps because I’m not a programmer by education, I always struggle to remember the syntax of programming languages, unless I’m working in them all the time. After I return to a language after working in other languages for a while, I always have difficulties remembering the syntax, and I spend some time feeling very frustrated.
Clojure and Lisps more generally are the exception. There is very little syntax, and therefore nothing to remember. I can pick it up and feel at home immediately, no matter how long I’ve been away from the language.
That's fair if you're looking at it from a performance perspective.
Not entirely fair if you look at it from a perspective of wanting fast feedback loops and correctness. In Clojure you get the former via the REPL workflow and the latter through various other means that in many cases go beyond what a typical type system provides.
> the opposite of boring
It's perhaps one of the most "boring in a good way" languages I ever used.
this is what i meant by that: https://news.ycombinator.com/item?id=47614353
This is only true if you assume C-like syntax is the "default."
But regardless of that, I'd argue that there's much less syntax to learn in LISPy languages. The core of it is really just one single syntactic concept.
The syntax argument is such a tired argument. With LISPy language there is almost zero syntax, it's pretty much executable AST.
Because of this, formatting matters a lot, but I don't think that's too different than other languages.
If you think LISP is hard to read, you are someone who could most benefit from branching out to a non-Algol lineage language.
Also, the little syntax present is pretty much timeless. Learn once and its yours for the next 50 years.
It's repeated a lot because it's true. The collective developer world has decided that LISP syntax is not the preference. Good if you prefer it, but you're the in the overwhelming minority.
Random example i just found via github explore: https://github.com/replikativ/datahike/blob/main/src/datahik...
You probably love it but to me it looks like a wall of text. Sure I can figure out what it does, but it gives me a headache.
but i bet if you sat down a junior developer not yet entrenched in any style yet, they'd be able to grok lisp code MUCH faster than the intricacies of syntax of the other alternatives ¯\_(ツ)_/¯
Javascript's destructuring syntax can look almost indecipherable, and it is mostly because the language syntax is not uniform in its meaning.
const f = ({a: {b: [x, , z] = [], c: {d: w} = {}} = {}, e: [, y] = []} = {}) => ({x, y, z, w});
This is a function written in one of the most popularly used programming languages in the world.People also put themselves into bubbles. Once in, they can filter out other languages (with all kinds of excuses), and be overly focused on certain families or only specific languages.
To use the right words: it’s not a syntax issue, it just looks unfamiliar to you.
To this day I have to look up whenever I get back into clojure what the "syntax" is of ns, require, import, etc.
The meta-programming power of Lisp may be largely due to being homoiconic, although Dylan/Julia etc achieve similar without it. However Lisp's minimal syntax is not a prerequisite for homoiconicity: S-Plus/R has a more conventional syntax while retaining "code is a list" representation.
(if (< a b)
(let [x (long-function-name a b)]
(another-long-function (+ x c)))
(+ a b))
or if a < b {
let x = long_function_name(a, b);
another_long_function(x + c)
} else {
a + b
}
to me the first one is way more noisy and confusing. and you really need a text editor with auto close and rainbow brackets to be productive, of course thats a non issue today with vscode and zed/neovim/helix but still something to think about. now rust might not be the best example for "easy to read syntax" but theres also python, lua, kotlin, even js if you ignore strict/loose equals and weird casts. all of them use more procedural/c like syntax because its the natural way humans think about running an algorithm. theres a reason why pseudocode in every textbook looks like that.> to me
> theres a reason why pseudocode in every textbook looks like that
The reason is history, inertia, and limited exposure. Preference is self-reinforcing for those reasons. That doesn't make it better, or irreversible.
Comparison with sledding is apt here, because both methods let you achieve the same goal - going down to the base of the mountain. Of course, skiing is more difficult to start with, it's more expensive, it requires deliberate effort and dedication. But the practical, learned experience far exceeds initial expectations. Do you realize how ridiculous it looks when inexperienced people try to convince them it's not worth it? Well, you may say, "the point is not to convince 'them', but to show the wider public..." And that's even more imbecilic. Imagine trying to point at people zigzagging 70 miles down from the peak, having enormous fun and telling the observers not to even try that? I'd dare anyone to argue with an experienced skier that sledding is more fun.
> theres a reason why pseudocode in every textbook looks like that.
Like I said, most - the absolute majority of Clojure programmers come to it after many years of programming in other languages (see the surveys). They are using it as a real instrument to achieve real goals, to solve practical problems. It's not an educational tool, not a "hello world app" incubator, not a "good for the resume" kind of a thing for them. If you (not you personally, but some proverbial programmer) are arguing just for the sake of it, well, with all respect, then "fuck you" (for wasting people's time). If you're sincerely trying to make a choice - nobody can "make a skier" out of you - that is something you must do on your own. No theory, no books, no videos can ever convey to you the enormous joy you may get out of it later - there's too much nontransferable tacit knowledge there. Just keep in mind, people in this community didn't make the choice because "their brains are wired differently" or something, not because "they are oblivious", no. Unlike you - they have seen, walked and lived through both of these worlds. Most of them have to switch between them, sometimes multiple times a day. And yes, the wider majority can often be wrong. In fact, history shows us that it makes wrong choices all the time. Lispers don't care about popular choices - they prioritize pragmatism above all.
You are right about that.
I value "minimal and simple" more than "familiar" because it makes my growth trajectory less arbitrary and more about intrinsic properties of the code. I don't care about learning how to do things the same way as everyone else nearly as much as I care about learning how code can be improved generally.
I know that code is written for humans, and that you can't remove the human from the equation. But I'm more interested in the future of code than in present-day code culture.
To be pedantic, this isn't quite correct. Syntax isn't countable like that. What S-expressions are light on is production rules. At their most basic they have IIRC 7 production rules but there are absolutely 0 languages based on s-expressions which are that simple, since it doesn't give you anything like quasiquotes, vectors, Lisp 2 function resolution, etc. Reader macros make matters much worse.
What we can say is that they are constructively simple, but not particularly unique in that. Once you get into real sexpr languages they aren't simpler than horn clauses, and are constructively more complex than languages like Brainfuck and Forth.
Even among lisps this has been problematic, you can look at common lisp's LOOP macro as an attempt to squeeze more structural meaning into a non-S-expression format.
Type's are for compilers ;) jk. I'm fully lover or type's but removing the constraint is easy in clojure. teams resist.
<3 the opposite of boring.
We mostly hired people with no previous Clojure experience. Majority of hires could pick up and get productive quickly. People fresh out of college picked it up faster. I even had a case of employee transitioning careers to S.E., with no previous programming experience, and the language was a non issue.
I can't remember an instance where the language was a barrier to ship something. Due to reduced syntax surface and lack of exotic features, the very large codebase followed the same basic idioms. It was often easy to dive into any part of the codebase and contribute. Due to the focus on data structures and REPL, understanding the codebase was simply a process of running parts of a program, inspecting its state, making a change, and repeat. Following this process naturally lead to having a good test suite, and we would rely on that.
Running on the JVM is the opposite of a problem. Being able to leverage the extensive JVM ecosystem is an enormous advantage for any real business, and the runtime performance itself is top tier and always improving.
The only hurdle I could say I observed in practice was not having a lot of compile time guarantees, but since it was a large codebase anyway, static guarantees would only matter in a local context, and we had our own solution to check types against service boundaries, so in the end it would've been a small gain regardless.
While it's amazing once you've learned it, and you're slurp/barfing while making huge structural edits to your code, it's a tall order.
I used Clojure for a long time, but I can't go back to dynamic typing. I cringe at the amount of time I spent walking through code with paper and pencil to track things like what are the exact keyvals in the maps that can reach this function that are solved with, say, `User = Guest | LoggedIn` + `LogIn(Guest, Password) -> LoggedIn | LogInError`.
Though I'm glad it exists for the people who prefer it.
you absolutely do NOT need to learn paredit to write lisp, any modern vim/emacs/vscode plugin will just handle parentheses for you automatically.
that said, if you do learn paredit style workflow - nobody in any language in any ide will come even close to how quickly you can manipulate the codebase.
It's like seeing that a movie is playing at the theater so you show up only to sit down next to people to explain your qualms with it, lolz. Sometimes you need to let others enjoy the show.
The OP of this thread even said all that needed to be said "The learning curve is steep but very much worth it" yet we're trapped in this cycle because someone had to embellish it with a listicle.
I take my post back.
Plus to be fair we're having this discussion in the context of an article from 2021 that just rose to front page of HN, only to repeat the same set of pros we've been hearing about Clojure for ages (code as data, repl, etc).
Probably should expect some dissenting opinions.
I agree with the short variable name convention, that's annoying and I wish people would stop that.
Everyone complains about a lack of type safety, but honestly I really just don't find that that is as much of an issue as people say it is. I dunno, I guess I feel like for the things I write in Clojure, type issues manifest pretty early and don't really affect production systems.
The clearest use-case I have for Clojure is how much easier it is to get correct concurrent software while still being able to use your Java libraries. The data structures being persistent gives you a lot of thread safety for free, but core.async can be a really nice way to wrangle together tasks, atoms are great for simple shared memory, and for complicated shared memory you have Haskell-style STM available. I don't remember the last time I had to reach for a raw mutex in Clojure.
Good concurrency constructs is actually how I found Clojure; I was looking for a competent port of Go-style concurrency on the JVM and I saw people raving about core.async, in addition to the lovely persistent maps, and immediately fell in love with the language.
Also, I really don't think the JVM is a downside; everyone hates on Java but the fact that you can still import any Java library means you're never blocked on language support. Additionally, if you're willing to use GraalVM, you can get native AOT executables that launch quickly (though you admittedly might need to do a bit of forward-declaration of reflection to get it working).
- Python: slow; GIL; dynamic; package management is shit; fractured ecosystem for a decade due to version split.
- Rust: borrow checker learning curve; compile times; half-baked async; too many string types; unreadable macros; constantly changing.
- Go: no generics for a decade, now bolted on awkwardly; noisy error handling; no sum types; no enums; hard to get right concurrency.
- Java: absurdly verbose; NPEs all around; JVM startup; enterprise culture;
- C+: Undefined behavior everywhere; header files; template err messages; huge lang spec;
I can keep yapping about every single programming language like that. You can construct a scary-sounding wall of bullet points for literally anything, without ever capturing the cohesive experience of actually building something in the language. For all these reasons, programming in general could sound like a hard sell.
Stop treating Clojure like a "hypothetical" option. It doesn't need your approval to be successful - it already is. It's not going away whether you like it or not - despite your humble or otherwise IMOs and uneducated opinions. It's endorsed by the largest digital bank in the world, it scales to serious, regulated, high-stakes production systems. Not theoretically, not conceptually, not presumably - it has proven its worth and value over and over, in a diverse set of domains, in all sorts of situations, on different teams, dissimilar platforms. There are emerging use-cases for which there's simply no better alternative. While you've been debating whether to try it or not, people have been building tons of interesting and valuable things in it. Clojure is in no rush to be "sold" to you or anyone else. It's already selling like ice cream in July (on selected markets) and you just don't know it.
Stopped reading here because of your hostility so I'll just say: yes I tried to use it "for real" but I didn't like it.
> because of your hostility
Clojure, just like pretty much every single language, tool, technique or paradigm does have its pros and cons, there's no denying that, but you can't just blindly come and shit all over someone's backyard expecting people to happily explain to you how inaccurate path your thinking took there. And it's not just a reaction to the post about Clojure - I'd defend any other tool the same way if someone did what you have.
> Stopped reading
If you don't have mental capacity to visually scan through four paragraphs of a response to your own remarks, that's pretty indicative. I guess you're not here to learn something new, but rather to assert your own perceived rectitude. Well, your perception is misguided. I suggest you correct it by learning more about the topic you so confidently trying to argue about, or respectfully and humbly STFU. If you think you know better than Goetz, Odersky, Kay, Steele, Felleisen, Friedman - that perhaps is not a good reflection. Just something to think about.
If you want to calcify something and add robustness, use clojure.spec or Malli. Clojure encourages writing testable code and also in general, there is less code to test. Smaller problem, easier to tackle well.
The JVM is a beast for serious things because of its performance and tooling. If you need something small/ with a quick start, you can use GraalVM or some of the dialects like ClojureScript or Babashka to do what needs to be done. There is ongoing work on ClojureCLR, Jank, Janet, Basilisp, Hy and other dialects or inspired languages. Usually, these are pretty close to Clojure or try to follow the behavior of Clojure so that stuff written using Clojure.core just works the same. Clojure is turning out to be the actual lingua franca.
For me, programming in Clojure is the nearest thing to fun that I ever had doing programming. To me there seems to be less ceremony about things especially on bigger projects. For the little things Babashka tends to be even more straight forward.
And yes, there are things about Clojure that can make the life harder. Usually it has to do with laziness e.g. when you just try to get a data structure written to a file. When you want to have restartable, stateful components such as database connections, web servers, etc. and want to start them in a certain order. There are some functions that are unexpectedly slow and stuff like this that could be somewhat more predictable. All this would be more approachable if there were real documents for beginners with a little more explanations than the terse descriptions that senior developers with 20+ years of experience find sufficient.
* Extreme thread-safety, better than all JVM languages and on par with the best.
* Macros - Other languages you have to reach for other languages to program in other paradigms, for example rules, logic programming, or writing a custom DAG. You'll probably say you don't need other paradigms whereas the truth is you just avoid it in other languages as it's too much of a hassle.
I nominate this sentence for "best inadvertent freestyle rap on Hackernews".
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.
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".
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
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!
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.
no amount of ide smartness or agentic shenanigans is going to replace the feeling of having development process in sync with your thought process
or toyota releasing a game engine: https://www.theverge.com/games/875995/toyota-fluorite-game-e...
(okay it's a design system, not so much a framework, but still)
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.
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.
https://clojure.org/news/2020/02/20/state-of-clojure-2020
The most recent report:
there's a detailed explanation here: https://youtu.be/Djsg33AN7CU?t=659
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)."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.
* 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.
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.
As in restarting the entire program and re-running every subsequent query/state changing mechanism that got you there in the first place, being careful not to accidentally run other parts of the program, setting it up to be just so, having to then rewrite parts if you then want to try something somewhere else?
Perhaps I'm misunderstanding you, because that sounds horrible unless you're scripting something small with one path.
The whole point of the REPL is that you evaluate whatever functions you need to get into your desired state and work from there. The ad hoc nature of it is what makes you able to just dig into a part of the code base and get going, even if that project is massive.
> Personally, I find waiting more than 200ms unacceptable and really < 50ms is ideal.
Calling a function on a running program takes microseconds; you're not restarting anything.
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.
(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.)
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.
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.
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
Not even close.