Skip to content

Comment on Idiomatic Clojure without sacrificing performance

Comments

A datapoint from a self-funded entrepreneur, for whom an app written in Clojure and ClojureScript provides a living:

Before you start discussing whether a language is "slow", check if perhaps it is "fast enough".

Apart from trying not to write stupid code, I don't even optimize anymore, because coupled with ridiculously fast servers (bare-metal hardware from Hetzner in my case), it's a waste of time. Things run great, and I'd much rather invest in building complex app features that customers will pay for, and have flexible living code that I can easily rearchitect as needed.

The last time I had to optimize Clojure code was when I wrote a search engine that had to respond to incremental searches in a fairly large E-commerce product databases. But that was also about 12 years ago, so computers were quite a bit slower, and JVMs were not as good as they are today.

I do tend to use transducers quite a bit, because they provide great composability and structure the code nicely, while providing a good performance boost as well (you avoid creating lots of intermediate data structures, which not only saves CPU, but also reduces cache, memory and GC usage) — but I don't worry about performance too much. It's just not a problem that appears on the radar.

Apart from trying not to write stupid code, I don't even optimize anymore, because coupled with ridiculously fast servers (bare-metal hardware from Hetzner in my case), it's a waste of time.

Inquiring mind wants to know: how do you deploy your Clojure (Script) webapp on your bare server? Just an "uber war" that you drop there? (bare servers at OVH here btw but I know Hetzner is good too)

Yes, I used a single uberjar for the first year or so, but then it got a bit more involved, because of delivery optimizations (nginx, brotli, precompressed files, versioned immutable static content with infinite cacheability, etc). So these days it's an uberjar (AOT-compiled) plus a bunch of files, essentially.

The servers are managed using ansible, and I also have a terraform setup for quickly spinning up a development cluster "in the cloud". Ansible takes a list of servers either from a static file or from terraform.

To add to this, you can also compile an uberjar into a static executable with Graal VM. I use that to build CLI apps for example, and it works wonders.

Keep in mind that your performance for long-running processes will get worse compared to just running a Uberjar as you're missing lots of optimizations going with GraalVM instead of Hotspot

*Unless you're paying for the commercial version

Worth noting, GraalVM's JVM implementation has a different JIT compiler which seems to give better performance for highly dynamic and polymorphic languages like Clojure and Scala

Yes, but in most cases GraalVM would be a preferable choice (https://www.inner-product.com/posts/benchmarking-graalvm-nat...).

Haven't read the full article but not sure it's accurate, the benchmarking script (https://github.com/inner-product/http4s-native-image/blob/ma...) doesn't even use the server VM and missing other basic optimizations you'd use if you were running a Uberjar in production.

So what does your app do? And what have been the biggest drawbacks from your language choice?

It's a PLM/ERP type app for electronics — https://partsbox.com/

Drawbacks, hmm — I'm not sure, really, there is no silver bullet, and I can't see significant problems with the language itself that another language would magically solve. I'm pretty happy with Clojure and ClojureScript and some advantages are hard to replicate elsewhere (such as business logic code being written only once and then shared between Clojure and ClojureScript).

One thing I noticed is I got spoiled by Rich Hickey solving problems for me, and I'd really like him to continue doing so. For example, transducers and core.async were great, but spec isn't quite finished yet. Also, reporting status and anomalies (as in, status of operations, computations, including errors/exceptions) and passing it through channels or foreign encodings is a problem I had to solve myself. I'm pretty sure Rich could come up with a better solution.

The biggest problems that I encounter everyday are not with the language itself, but with the ecosystem. Many libraries could use more maintenance. And the really big problems aren't language-related at all. For example, RethinkDB shutting down. I also wish I didn't have to write the full FoundationDB integration ("language binding") myself.

AboutSource Built by g1lg1l

Hackerly is an independent reader for Hacker News, built on the public HN API. Not affiliated with Y Combinator.