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 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.
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
trialing
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 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 assessing
malli
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.