Skip to content

Instantly share code, notes, and snippets.

@lemonteaa
Last active September 16, 2022 15:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lemonteaa/7165a89442eed5c850c54934aaa01eaf to your computer and use it in GitHub Desktop.
Save lemonteaa/7165a89442eed5c850c54934aaa01eaf to your computer and use it in GitHub Desktop.
What a good fullstack Clojure(script) looks like in 2022?

Advanced Baseline (Clojure(script)) 1

Clojure(script) 2 have matured as a language and is no longer as hyped. At this point in time, there ought to be consolidation towards an easy path (Ya, Simple not easy 3 I know, but try telling that to someone in a rush (me)). This notes sketches one to the best of my personal knowledge.

Disclaimer: I haven't followed it up in the last 2-3 years, so it may have been lagging behind for one full generation of tech. That being said, Clojure tends to be more stable, slow, and methodical in its design process, so libraries tend to have a longer shell life.

Context/Assumption

  • You're in a greenfield project, with progressive management, and so are free to aggressively adopt best-in-class architecture/design/practises.
  • You want to have a mostly stable base upon which to build - sacrificing some "advanced features" is acceptable. This affect the selection of libraries.
  • You do want to maintain interoperability with the mainstream world - therefore at the API level the choice of Clojure should remain as invisible as possible.
  • (Backend) You want an "API-first" approach - start by writing the API spec in a technology neutral format, then derive downstream tooling etc.

Backend

For web server, pick pedestal. It strikes the right balance between advanced features and stability - although in 2022 it seems more like a stop gap. Then, swap out the routing with the modern library reitit, which features data-driven approach as well as a robust ecosystem with support for pedestal integration. (If one is willing to experiment, may also consider bidi, which is broadly similar in philosophy minus the ecosystem)

On the database layer, choose an Event-Sourcing Architecture and use xtdb (formerly crux). This let you use all the datalog goodies, while also enjoying a nice semantic model around issues of transaction and concurrency.

For authentication, the oldies buddy-auth is good enough, and pedestal's list of official example include one that port the middlewares into interceptor 4.

API is considered to be the weakest link in our stack thus far:

  • For GraphQL, walmartlabs/lacinia-pedestal (Lacinia's integration with pedestal) does what we need and is mature and actively maintained.
  • For OpenAPI (formerly Swagger), sadly we have to DIY. Among the more recent libraries that are trying to fill this gap (not pedestal-specific), malli seems to have the most momentum and even have a PR that mostly does what we want (converting a JSONSchema into a validator).

Other stuff:

  • Structured logging
  • Metric/Monitoring (Strong part of pedestal as it has built-in support)

Extra Note:

  • Need to do JSON serialization yourself? Just add any library, like chesire or the more recent ones.

Future?

On the backend side I feel like Clojure is indeed suffering from growing pain and is in an embarassing transitional period - the old, established libraries are showing its age and cannot catch up to latest development in tech. Meanwhile, the next generation of libraries face the risk of fragmentation and lack of momentum. It is my hope that eventually a more well-rounded, mature, well-maintained solution emerge.

The main risk is the choice of pedestal. If everything goes right, it should be replaced by yada, a next generation web server/library with a focus on HTTP compliance (eg Content Negotiation), while also supporting the selling point of the last generation such as async. Unfortunately, momentum seems to have stalled. (For that to happen, one of the hurdle I foresee is the need to replace all those integrating plugins/libraries/tools that currently exists for pedestal, either as new plugins, or battery-included in the main library itself)

Another point to consider is the Ring standard itself. Ring 2.0 spec is in a drafting stage, and will make async official. The best outcome would be if it becomes finalized, then modern libraries like yada (as well as the lower layer http/app servers) adopt and fully support it.

Tooling

While lein is the old guard and still going strong, its main competitor in the last age, boot 5 seems to have lost completely - it is no longer actively maintained. Meanwhile deps.edn 6 is an officially supported method for tooling introduced in more recent version of Clojure the language.

The trends seems to be gradually migrating towards the official method (one less extra moving part is one less thing to worry about). Still, the reality is that one may be unable to just shake off lein immediately as it still offers the ecosystem/support, and have many advanced features. As an example of a transitional solution, consider lein-tools-deps.

Another thing: tap> is a recent innovation to increases visibility into Clojure programs. Upon this foundation it is expected that the next generation tooling may offer something to match debuggers of other languages. (Keeping in mind the historical influence the REPL had have on Clojure)

References

https://www.thoughtworks.com/insights/podcasts/technology-podcasts/future-clojure

https://github.com/peykio/clojure-experiment/blob/main/README.md

https://practical.li/blog/posts/practicalli-plans-for-spring-2022/

https://www.juxt.pro/blog/radar-2021

ring-clojure/ring#393

Footnotes

  1. Why "Advanced"? We're doing an API web server with modern frontend, so no luminus.

  2. Clojure is the main language and runs on JVM. (Yes, Java runs on JVM too - Clojure belongs to a family of language that aims to leverage the good part of JVM the platform while improving upon the perceived severe short-coming of Java the language) Meanwhile Clojurescript is the same language syntax/semantics (mod some differences) ported to compiles down to Javascript so that it can run on browser. In short Clojure's for backend and Clojurescript's for frontend.

  3. Title of a pivotal talks by Rich Hickey that become a key part of the Clojure philosophy.

  4. Ring is the spec/interface standard of web in Clojure, and that's why the library offer a Ring middleware. Pedestal uses interceptors instead, which is even more flexible but can be harder to understand.

  5. In short lein takes a declarative approach while boot takes an imperative approach. An analogy would be Maven (declarative) vs Ant (Imperative) in Java. The usual consensus is that declarative/Convention over Configuration (yes, I'm betraying my age here) is good, but if you have very custom needs, an imperative style is more honest and requries less contortion.

  6. The actual tool is a command line program called clj. The name refers to the file used to specify dependencies. The extension refers to Clojure's own language-native data format edn (Extensible Data Notation), which is a counterpart of Javascript's json (Javascript Object Notation).

@malcolmsparks
Copy link

Is taps> a typo? Shouldn't it be tap>?

@lemonteaa
Copy link
Author

Ar, my bad. Updated, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment