Radar
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.
Rings
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.
Assess
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.
Trial
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.
Adopt
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.
Hold
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.
Quadrants
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.
ClojureScript
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 visualization
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
visualizations, building dashboards and reports, and sharing
visualizations instantly via GitHub gists and the
Vega Editor.
Building ClojureScript applications seems to get easier
every year, and here we adoptshadow-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.
Infrastructure
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 modeling 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 recognize 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 recognize 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 trialing
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.
Tools
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 trialing
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. 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 favorite and the Clojure goes fast!
blog an excellent resource.
Libraries
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
trialingMuuntaja
(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 trialing
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 favors a
data-driven, explicit approach to configuration, with support for
externalization of secrets.
next-jdbc
is a complete, modernized 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 assessingmalli
and of course, in the future, spec2.
For logging, our preference is to maximize 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.
Honorable Mentions
The radar isn’t exhaustive, and where we’re pushed for space we tend to prioritize those libraries that are on the way up or on the way down. This leaves some of our regular favorites 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 serialization libraries available, for all manner of uses in Clojure and beyond.
Contributors/Reviewers
Our thanks to all at JUXT, and the wider JUXT network, who have given opinions and suggestions for this radar.