Back in 2017, when dinosaurs roamed the earth and I got my first coding job, I was super excited about Clojure. And what’s there not to be excited about? It’s super fast, it’s concise, and it’s a lisp. But now I’m coding in Ruby. Why?
Superficially, Ruby and Clojure are similar (bear with me): They’re high-level, they’re really well-designed, and they’re general purpose languages. People like working with them.
But, in my opinion, there’s an extremely subtle distinction: Clojure is trying to be a perfect language, while Ruby aims to be a perfect language for getting things done.
You often see it in the readme of open source projects.
A typical Ruby project readme could be:
Here, run these three magical commands and your web application supports Sign in with Google. Ah, and if you want to customize something, check out these options. All set? Cool, have fun!
Whereas a typical Clojure readme could be:
After benchmarking the speed of routing libraries, we noticed that there was potential for improvement. Now our routing library is based on an inverse voodoo tree which makes routing 80% faster. Want to get started? Um, check out the source code for the API.
This is so interesting. Clojure folks tend to get enchanted with the exact same things which pulled them into the language: Building something perfect. Something super clean, at the right level of abstraction, with great performance, it’ll compile 100 years from now, an alien civilisation will later marvel at your lisp macros, etc, etc. One consequence of that is that Clojure libraries, trying to adhere to the functional philosophy, try not to have a whole lot of magic.
Like, there isn’t a full-blown web framework in Clojure - because that would require the web framework developers to make a whole lot of decisions, and Clojure library people prefer not to impose their decisions on their fellow developers. Which is a fine decision (chuckle) to make! And, to be fair, that’s a breath of fresh air with all the clunky JS libraries out there.
But that also means that, if you want to build a web app in Clojure, you have to make a ton of decisions yourself. Like, what routing library do I use? How do I handle static files? Which HTML templating language do I choose? Does my API serve JSON or EDN?
And that becomes tiring really fast.
Okay. Now let’s look at Ruby for a moment. What are Ruby libraries about? While Clojure libraries tend to aim for perfection, Ruby libraries aim for getting stuff done and making people happy. Those two things actually overlap: If you enable people to get stuff done, they’ll be even happier.
Getting stuff done will require decisions to be made, and those come with trade-offs. Like, if you want to enable your users to build a web app, you’ll have to choose the routing library beforehand. You’ll also decide on how to serve static files, how to handle HTML templates, and to serve JSON on your API.
Clojure people would protest. This is tightly coupled! Too opinionated! Too much magic!
But that’s exactly the point. If we want to optimize for getting things done, we have to make some hard decisions first when abstracting the low-level stuff away, and then move on. Ruby libraries make those decisions for us.
Now you might think that this is why Ruby is generally better than Clojure. Far from it! It’s only one aspect when comparing them. And it depends on your situation whether this aspect matters at all.
For me, it mattered a lot. As a solo developer developing web apps, Ruby (and yep, Rails) is such a boost to productivity that it’s simply negligent to choose anything else. I want to sit down and crank out HTML. I don’t want to fiddle with routing libraries, static file serving and SQL queries.
Want to add Sign in with Google? Sure, here’s a magic library and it just works! So cool.
But that also means that a lot of my coding time is spent wading through documentation and searching for code examples of how a certain library should be used. That’s not always easy and it always requires an internet connection.
Wait, why are we talking about internet connections? Because Clojure coding is different. Clojure has airplane productivity.
I love to think back to the days when I was coding in Clojure. Those were truly magical (Ruby pun intended here). Like, when I was coding a medical image viewer at my past company, I could just sit back, elegantly type some lisp, and think about the problems I was trying to solve. The language got completely out of the way, to the extent that I could be completely offline. That’s because the Clojure standard library is so great and you simply don’t need much else. Everything can be modeled as a map or a vector, and then you have all sorts of fancy functions to transform those. That’s all you need.
So, while a grey-scale image might simply be a two-dimensional vector in Clojure, it might be
SomeMagicalObject in Ruby. In Ruby, you’ll need to check out the documentation to understand which methods
SomeMagicalObject has and how you should use them. In Clojure, you already know the data structure, you
already know all the functions, and you just start solving the problem.
And that illustrates the problems for which Clojure is a perfect fit: Problems where you are not so library-dependent. Like implementing algorithms, building compilers, or simply solving very hard problems.
But not web apps. Web apps are probably the most library-dependent projects humans have invented so far.
That’s where Rails comes in.