Clojure Frameworks: opinions are features, not bugs
Ever since I wrote Clojure needs a Rails, folks reach out to ask for my opinion on their web framework for Clojure. What's clear from these discussions is a lot of software developers have no idea what a framework is.
Dear Janet, we decided to build our own Clojure(script) framework called Enjure because none of the existing options suited our needs. We're committed to the success of Enjure, but our investors think we're distracted from our core business. Can you help us figure out how we can sell our investors on building something we can't sell? Thanks, Jimmy James CTO of SuperREEL.
Ever since I wrote Clojure needs a Rails, folks reach out to ask for my opinion on their web framework for Clojure. What's clear from these discussions is a lot of software developers have no idea what a framework is. Frameworks follow a principle called "Inversion of Control", where developers delegate crucial decisions for their application to the framework maintainers, allowing the developers to deliver the application faster. It is not a project template, a new paradigm, or module system.
More Decisions, More Problems
Often, developers suffer from decision paralysis and excessive engineering prowess. I understand why developers are like this. I was like this too (see my design patterns posts for years of pent up engineering hubris). Unfortunately, we are in the business of solving problems and making money with those solutions, so the sooner the better. We get there sooner by not bothering with things like architecture, databases, configuration, convention, and tooling, in other words, we can just get on with the coding.
Architecture
Arguably, the most important decision delegated to your framework is the software architecture, and many consider it the defining feature of a Software Framework, otherwise, you might just be using a software developer kit (SDK), bundle of libraries, or some toolkit. All of those are fine to use, but I doubt they'll be your next Rails.
Database
How frameworks handle databases tell me if you mean business. I don't care about how it gets done, honestly. I want a defentity
macro (or something) and through the power of macro magic we get CRUD handlers (controllers?) for this entity, a dashboard to manipulate them, and hooks to control access. The core idea here being abstraction, why do we have to write the same queries over and over again? Most databases can be abstracted away in a data-mapper or ORM, I know those are dirty words to the Clojure community, but something truly great would give us the same powerful abstraction to create database things while allowing us to choose our database with an opinated plugin.
Tooling
Our toolchain and libraries fit the mould, our opinated MySQL or Datomic plugin just works, because it's made by the framework maintainers, and allows the same macro magic to create our entities. Notice how these plugins don't have the caveat of "you can just replace the whole thing with modules/plugins!", if I wanted to do something like that, I would, yet again, start a project from scratch withdeps.edn
and the Clojure CLI. A bundle of libraries and glue code can masquerade as a framework without direction or purpose, so beware, you might end up configuring your whole stack anyways.
Configuration
EDN files, database migrations, environment variables, feature flags, software versioning, docker compose, what the hell are we doing with our time. Ask any Site Reliability Engineer(ing specialist for legal purposes), and she or he will tell you that configuration mishaps are responsible for a vast majority of incidents and outages, and we're still writing them all by hand over and over again. One snake_case name, where it should have been kebab-case, and now VP of Engineering is breathing down our Zoom call necks.
Convention
Thanks to Clojure's dependence on camelCase virtual machines and their libraries, sometimes our conventions breaks. Hell, sometimes developers just don't care, yet developers make decisions every second of writing code and rely on those conventions to ship a functioning product. The cognitive overhead of using the REPL to dig into a software rotted library to see they keywordized the camelCase keys from JSONland not bothering to transform them to our beloved kebab-case (or worse, mixing them) causes bugs and slowdown.
Opinions are good, actually
If the past several paragraphs were any indication, opinions are good. They might not be scalable to every project, but I think there will be opinions to fill the void at some point. I'm writing this article because I see an awful lot of scoffing at the idea of something opinionated in the Clojure community. If you don't want something opinated, that's fine. Personally, I do not have the ego to say I'm smarter than every other developer, so let me rewrite everything that has ever been written once again. So let me ask you dear reader, does this really command the pay of an expensive, paren wielding, high-value producing, knowledge worker?