Welcome to The JUXT Clojure Radar, 2021. This is an opinionated and subjective view, based on our collective experience of building projects with Clojure. There will be many libraries that should be on here but aren’t, and this is partly down to the awesome Clojure ecosystem and how we take some of it for granted.
Whilst we have direct experience of the majority of items on the radar, we may include some that we haven’t used. We will explicitly mention this where appropriate.
No radar can ever be perfect, but we hope that this radar will be useful for those deploying Clojure in real-world projects, and we welcome your feedback and suggestions. We produced a similar radar five years ago, and it has been interesting to see how the Clojure ecosystem has shifted since then.
Our radar is derived from the well-known ThoughtWorks Technology Radar and we use the familiar four rings defined there: Assess, Trial, Adopt and Hold. Our working definitions are a little different to the ones ThoughtWorks use.
We’re interested in this library or tool. We think it could have great potential but we have not yet formed a strong opinion. This may be a library we have not yet used, or one that we have only begun to explore.
We have some experience with this library or tool and we like the results. We’re not yet certain about how widely applicable it will be, but we plan to continue to use it on projects where the risk is low.
We’ve used this library or tool extensively. We like it, and prefer it to alternatives. We think it brings great benefit to our projects and we’ll use it without hesitation.
We have experience with this tool or library on past projects and we feel that we may not opt to use it again in future, or at least think very carefully when we do. This may be because we have a growing concern about its impact, or we simply prefer an alternative.
We’ve divided the radar into four quadrants. ClojureScript and Tools should be self-explanatory. Infrastructure refers to a group of libraries that have a high architectural footprint or influence on your application. The smaller libraries, we’ve simply put into Libraries.
Reagent is one of the few libraries that appeared on our radar in 2016 that we still want to shout about 5 years on, and is a firm Adopt. It has brought huge value to ClojureScript and made delivering simple React applications easier in ClojureScript than any other language. We’ve also found re-frame to be one of the best ways to keep complexity under control and maintain a consistent and logical structure as ClojureScript applications grow.
We’ve begun using re-frame-10x to bring greater productivity to working with re-frame, and we have also had good early success using kee-frame to build re-frame-style applications more easily. Both are in Trial.
Oz brings the power of Vega and Vega-Lite's declarative visualisation grammar seamlessly to ClojureScript. ClojureScript's interactive, data-driven programming approach is a great match for Vega, and Oz brings these worlds together with some additional features for composing visualisations, building dashboards and reports, and sharing visualisations instantly via GitHub gists and the Vega Editor.
Building ClojureScript applications seems to get easier every year, and here we adopt shadow-cljs and Figwheel Main to do so with great success (we see no reason to continue using lein-figwheel, figwheel’s older incarnation). At JUXT we lean towards shadow-cljs. As the ClojureScript compiler becomes more capable we’ve seen a trend towards simpler ClojureScript project build tools like krell, which provides a minimal path to building React Native apps (although we have not yet tried this route, we would like to assess).
Devcards is an excellent tool to interactively test and iterate quickly on ClojureScript UI components. For full, in-browser automation, we’ve found Cypress makes creating and running tests a breeze compared to older tools (and we like it so much we’ve included it in our radar at Trial, despite it not being a ClojureScript tool).
At JUXT we’re interested in pushing the limits of ClojureScript performance as part of building demanding applications where render speed counts. We’re assessing new libraries like Helix that have potential to improve on the best we can achieve today with hiccup.
Fulcro has piqued our interest, as we think there is great potential for a graph-centric, data-driven approach for queries and mutations at all layers of the application. However we’re also concerned about how whole-application frameworks like Fulcro affect our ability to understand and debug the systems we build. We’ve yet to assess Fulcro in a real-world application.
There’s been healthy competition amongst libraries that offer to wire up your application and manage the stateful parts. We’ve used Component in the past, but we feel that Integrant has brought greater flexibility to this space and we have adopted it as our first choice. We’ve found that Mount encourages more code that depends on global state, and we prefer how the alternatives approach this problem, so at JUXT we have placed it on hold.
Ring remains the dominant HTTP abstraction for Clojure, with the ring family providing an out-of-the-box HTTP server, middleware and more. Ring has been battletested on many large projects and we continue to feel confident using it. We are struck by the power and value the Ring specification has brought to Clojure. In this area we have also decided to move one of our own libraries, yada, to Hold, and we’ve written on the rationale as part of launching a newer JUXT library apex. Apex continues to allow modelling web resources with data, to build compliant web resource servers, but builds on a more widely used and accessible data model in OpenAPI and JSON Schema.
After some debate, we’ve decided to move core.async to Hold. We recognise that this may be controversial, but through experience of working with systems that build heavily on core.async we’ve grown concerned that the results are often fragile due to incorrect error handling, hard to understand, and hard to debug. core.async and similar solutions are often used where a simple thread pool and blocking techniques would suffice. We recommend thinking carefully before creating applications that have an architecture driven by core.async, and we wanted to highlight this as part of the radar. Tim Baldridge has spoken in the past about some of the trade-offs to consider and pitfalls to avoid. Let's be clear, core.async is a powerful tool and a great asset to Clojure, and may now be on the slope of enlightement as Clojure’s essential asynchronous glue. However, if we can borrow the ThoughtWorks definition of Hold for a moment, proceed with caution.
core.typed we have placed on hold for our own projects, since we've found clojure.spec hitting a sweet spot with gradual, runtime verification and we have noted concerns in the industry as projects grow. We had success assessing typed Clojure in the past, and we recognise a great many improvements that have been made in recent years, now in the Typed Clojure project. We hope to revisit Typed Clojure with Clojure 1.11.
We placed XTDB (formerly Crux) in Adopt, as we believe it brings unique and enduring value to our projects. Disclaimer: We wrote XTDB. Expect a bias. We have also introduced site, a compliant HTTP Resource Server built on top of XTDB, and after trialling site on JUXT projects we think it is showing great potential to accelerate delivery.
We’re keen to see Clojure pushed to new frontiers, and we think holy-lambda could give us an improved implementation technique for Clojure lambdas on AWS with fewer compromises. We’re keeping holy-lambda at Assess, and in the meantime, we’ll continue to use lambada and cljs-lambda.
Polylith has made waves in the community with a fresh take on maintaining and building modular applications from monolithic repositories. We have yet to try this approach but we’re keen to assess.
We’ve included ParEdit on our radar, but it’s structural editing that’s the star to adopt here, no matter what tool or plugin you use to achieve it. Despite structural editing commands being used with Lisps for at least 40 years, the state of the art is still being progressed with tools like ParInfer. In our editors clj-kondo has become indispensable and a firm Adopt, instantly helping to improve code quality and highlight those pesky errors before they waste our time. Most at JUXT are long-time users of Emacs and CIDER, so we’re excited by efforts like Clojure-lsp, and the new ways in which our Clojure editing environment is being enriched. Some at JUXT are trialling Clojure-lsp via Calva in VS Code.
We’ve loved following tools produced by the indefatigable borkdude, and we think babashka is a truly transformative tool that’s ready to adopt. It has quickly replaced bash wherever scripts are useful, and further cemented Clojure as a universal language for teams. babashka 'tasks' are a powerful but relatively recent addition that appear to have great potential to replace make-like tools with more Clojure. We plan to assess futher. We’ve explored this space ourselves with mach, and we were pleased to see babashka tasks able to encompass the same use-cases.
We have many older Clojure projects that use Leiningen, and a small number that use Boot (now on hold), but in recent years we’ve switched to deps.edn and clj. We like that builds are simple and fast, and since tools around deps.edn have accumulated rapidly we now use it confidently on all new projects, so we’re placing deps.edn in Adopt. We’re also increasingly using kaocha to run our tests, and after a boost of support from Clojurists Together in 2018 we think it has become a good, all-encompassing solution in a way that no previous efforts have.
Ragtime has stood the test of time as a flexible database migration tool so we’re happy to place it in Adopt.
In the performance analysis space jmh-clojure has become a go-to tool, now in Adopt, and the comprehensive results it produces from a simple configuration are impressive. Of course criterium is a long-standing favourite and the Clojure goes fast! blog an excellent resource.
Metosin has been working to create a new family of Clojure web API libraries delivering excellent performance. We’ve found reitit to be a highly effective routing library, so much so that it has unseated our own router bidi to enter Adopt at JUXT. We’re keen to continue trialling Muuntaja (a very fast HTTP middleware stack and alternative to ring-middleware-format) and Jsonista (Clojure’s fastest JSON library), and we’re pleased with results so far. For client-side HTTP, we've been trialling Hato, a library in the style of clj-http, but built on Java 11's new HttpClient and with fewer dependencies as a result.
We’re often interacting with AWS and find Cognitect’s aws-api to be an excellent choice for doing so. Its minimal dependencies help keep our projects small and sane, and we have begun to prefer it over amazonica unless we need functionality that’s unique to the AWS Java SDK(s) (upon which amazonica is based). Babashka also offers an aws-api pod, creating great opportunities for AWS scripting.
We’ve moved our own Aero configuration library into Adopt as we’ve used it widely on JUXT projects for many years. Aero favours a data-driven, explicit approach to configuration, with support for externalisation of secrets.
next-jdbc is a complete, modernised replacement for clojure.java.jdbc. We’ve used it both on JUXT projects and in XTDB.
Buddy (in the form of buddy-auth and buddy-sign) has proven a successful choice for JUXT projects. As a piece of critical security implementation we preferred in 2016 to give it more time in the field before moving it to Adopt. Since Buddy has had another 5 years of widespread use and maturation, we’re deeming it a good choice, however the project is looking for new maintainers.
We’ve placed Schema on hold, as we have found that clojure.spec not only offers more powerful sequence validation via regular expressions, but also has a healthy community of actively maintained tools supporting and extending it such as Orchestra, expound and spec-tools. We’re interested in assessing malli and of course, in the future, spec2.
For logging, our preference is to maximise performance and compatibility. We’ve found the best and most enduring way to do this on the JVM is a combination of tools.logging and Logback, and so we’ve moved timbre to Hold. Although Logback configuration — especially getting the right selection of bridges and exclusions in place — can be a little fiddly.
tick is proving to be an effective clj-time replacement on JUXT projects, in particular since it builds on cljc.java-time to support both Clojure and ClojureScript and this allows us to share time-related functions across client and server.
GraphQL has been a powerful, simplifying model for some of the most complex web applications we’ve built at JUXT, and although the Clojure ecosystem around this technology is relatively small, we’ve found the data-driven approach of Lacinia to be highly effective and the library itself is mature with good coverage of the GraphQL specification. Users of GraphQL should also check out re-graph by JUXTer Oliver Hine.
The radar isn’t exhaustive, and where we’re pushed for space we tend to prioritise those libraries that are on the way up or on the way down. This leaves some of our regular favourites out, including clj-http, HoneySQL, weavejester’s crypto libraries (1, 2, 3), and Jackdaw, a comprehensive Kafka Producer, Consumer and Streams API client. Nippy is one of the best serialisation libraries available, for all manner of uses in Clojure and beyond.
Our thanks to all at JUXT, and the wider JUXT network, who have given opinions and suggestions for this radar.