Clojure needs a Rails, but not for the reason you think

I think there's another reason this problem persists in Clojure. There's no Rails for Clojure.

Clojure needs a Rails, but not for the reason you think
Photo by Julian Hochgesang / Unsplash

First, a quick story

Suzi, a software engineer of suitable caliber, loves writing Clojure. At her polyglot employer, she often uses her technical freedom to bat for Clojure every chance she gets. One day her manager taps her for an opportunity, the company wants to build a scrappy, lean internal tooling team to build tools for sales, marketing, and so on.  Perfect.

One the first day of the tooling team, the marketing team wants to move the website to something custom and use Stripe for payments from the lower segment customers. No problem. Clojure's web stack is very mature and will be sinch to whip-up this project for marketing. The website was easy enough to tackle, a little ring here, a little hiccup there, and presto! She has her website after just a couple of weeks of coding.

Next on her docket was the Stripe payment integration. Should be the same thing as the website. Suzi sees there are a number of Stripe Clojure libraries to choose from.

"Perfect" Suzi thinks to herself as she were in developer heaven.

Upon her first test with the repl, she gets an exception. She inspects her Stripe library by looking at the master branch on Github: Last commit 7 years ago. F**k. It's just some simple requests with clj-http and cheshire. Next library. Last commit 3 years ago.

"Okay, looking better" Suzi thinks to herself as she lets out a sigh of relief.

This library is almost the same thing as the last one, simple and only covering a fraction the API. Unsure if this new library actually keeps with the current API contract, no support for lazy pagination like the Java SDK, and it suffers from the Log4j vulnerability. Fantastic, so much for that estimate. The next day during stand up, Suzi explains to her colleagues that the project is going to take a bit longer because she didn't anticipate having to update libraries and implement features the Java crowd gets for free.

Fast forward a couple months. After the marketing website hurdles, a new project from IT is requested. They want a syslog relay for SOC2 monitoring stuff. That doesn't really sound like SOC2 compliance to Suzi, but she has her Clojure hammer and a good salary, so she gets to work. Once she finishes the requirements, she begins researching to give an estimate of how long the project will take, only to find there is no syslog server library for Clojure that she can find. She will have to write one herself. During stand up the next day, Suzi delivers the estimate to her team with her line manager, Sam, present.

"It'll probably take two months to build this relay because there's no syslog server library for Clojure." she says.

"Oh wow, I didn't think it would take that long." Sam says.

Dillion, a colleague in the standup, says "Go has great syslog server library and even supports Rsyslog! Maybe you could use that".

"That's probably a good idea, Suzi can estimate how long it'll take using Go?" Sam replies.

Later that day, Suzi estimates the project with Go instead. The business case for Go is very strong now. Sh*t.

Strange yet familiar

If Suzi's story resonated with you, you're like me and dozens of other Clojure developers. Clojure suffers from missing or rotting software libraries a lot. Developers love writing code, but it's really hard to sell management on building an entire library when another language has so many batteries built in.

In a perfect world of open-source software, companies would give back to the community by building libraries they need and licensing them in the wild. While some software companies do this, in my experience, it's usually the opposite that happens, most companies don't give back, but I think there's another reason this problem persists in Clojure. There's no Rails for Clojure.

Railing against Rails

Because there is no Rails for Clojure, a lot of open-source software efforts in the Clojure community go towards developing web frameworks, libraries, toolkits, and so on, trying to fill this "Rails for Clojure" vacuum. Yet, the web stack for Clojure is very mature.

- Ring (server)
- Compojure (router)
- Edge (framework)
- Bidi (router)
- Yada (library, bidi + aleph)
- Aleph (server)
- Reitit (router)
- Luminus (many libraries)
- Kit (framework)
- Pedestal (many libraries)
- Selmer (templates)
- Hiccup (HTML DSL)
Non-exhaustive list of Clojure web stuff in no particular order

Every time I see Clojure on the front page of Hacker News, it's a post talking about a new web framework, library, toolkit, or abstraction for Clojure, and (usually) the maintainer has abandoned the project since revealing it to the world. My theory is, this causes a lack of effort expended on mundane libraries. It's not the most fun thing to build. I get it. Really, I do, but lack of these API-wrapping and protocol-handling libraries put Clojure at a significant disadvantage in the business of software.

Interop for the mundane

Why not just use Java and Javascript interop for the mundane, everyday libraries, is there something wrong with that?

It's possible, but interop comes with caveats.

  • Interop can be frustrating with big frameworks.
  • Interop relies heavily on reflection, and may require type hinting once it becomes a performance problem.
  • Interop implies the use of side-effects since we're relying on Java objects.
  • Java sequences and Collections may require a clojure.walk/postwalk and zipmap with coercion to get a good Clojure container/sequence/collection.
  • No variodic arguments to deftype or defprotocol for technical reasons, meaning overloading isn't happening when extending Java types.

If we used interop for everything mundane, Clojure would really just be an S-expression shaped husk over Java code. Not a very good solution.

A lot of the mundane Clojure libraries do actually wrap a Java SDK, giving us a clean, functional interface to work with. I think this is fine, but tough to keep up with if the right abstractions aren't used. Amazonica, a Clojure library for AWS, does this quite well by using macros to intern new Clojure functions that wrap Java methods, transforming types between Clojure and Java land. It takes a single function call to turn a new AWS package into a set of usable libraries.

Rallying on the tracks

Other programming languages have their definitive web framework. Ruby has Rails, Python has Django, Java has Play, Elixir has Pheonix. I know there are other web framework, libraries, toolkit, and so on for each of these programming languages, but their communities always seems to rally around each of these projects. Even a language as niche as Elixir, has it's own "Rails" (so to speak).  

Why doesn't Clojure do this? From my observation, the Clojure community seems a bit more de-centralized and laid back. It's awesome, but can we please just get together at the next Conj conference decide what our "Rails" is going to be? That way we can get down to business.

As always, You can also join the discussion about this post on twitter, Hacker News, or reddit if you think I'm wrong. If you like what I have to say, subscribe for the next post, follow me on twitter @janetacarr , or don't ¯\_(ツ)_/¯ .

Subscribe to Janet A. Carr

Sign up now to get access to the library of members-only issues.
Jamie Larson