deps.edn and monorepos VII (Polylith)
This is part of an ongoing series of blog posts about our ever-evolving use of the Clojure CLI,
deps.edn
, and Polylith, with our monorepo at
World Singles Networks.
The Monorepo/Polylith Series
This blog post is part of an ongoing series following our experiences with our Clojure monorepo and our migration to Polylith:
- deps.edn and monorepos
- deps.edn and monorepos II
- deps.edn and monorepos III (Polylith)
- deps.edn and monorepos IV
- deps.edn and monorepos V (Polylith)
- deps.edn and monorepos VI (Polylith)
- deps.edn and monorepos VII (Polylith) (this post)
- deps.edn and monorepos VIII (Polylith)
- deps.edn and monorepos IX (Polylith)
- deps.edn and monorepos X (Polylith)
- deps.edn and monorepos XI (Polylith)
Part VII
Another update, less than two weeks since the last one? Yes! Polylith has had another alpha release with a very cool new feature added, we've completed our first multi-implementation component in Polylith, and my development workflow has had a major facelift with the latest releases from Portal!
Portal
Let's start with the development workflow. I've been using tap>
in my development
workflow ever since it was added in the Clojure 1.10 prerelease cycle (Fall 2018 I think?).
When Cognitect's REBL came out, I loved what could be done with datafy
and nav
and I also loved that it began to keep a tap>
history so you could browse through
all of that data. I used REBL for a long time before switching to
Reveal and I've been enjoying using that on both macOS and Windows,
although having a WSLg-powered X window for Reveal is a much less pleasant experience
on Windows than the drop-snap behavior for fullscreen apps on macOS.
I'd looked at Portal fairly early on in its life and really didn't like that it
required a browser window for rendering but recently decided to take another look
because it was clear that Chris Badahdah (@djblue)
had put in a lot of work since then. It turned out that Chris had been considering
a VS Code extension that encapsulated Portal into a webview panel and I got to test
an early .vsix
file and, for me, that was a game changer! I no longer needed my
VS Code window to share space with either a JavaFX app or a browser: I could have
my data visualizer right there inside my editor and move it around like any
other tab, and have my editor be completely full screen on both macOS and Windows!
My core workflow with tap>
hasn't changed:
my dot-clojure
repo still has a dev.clj
script that starts a Socket REPL and Rebel Readline etc
and my VS Code/Clover setup
repo still has all the same key bindings and config.cljs
file that sends all of
my evaluated code to tap>
. But now everything happens inside VS Code. I've added
a key binding (ctrl-; shift-p
) that runs a Portal start
task, which uses the
REPL to launch a Portal window inside VS Code (see the
Portal extension for VS Code)
and adds a few extra custom commands to it (via a nice custom predicate/function API!).
Best of all, my workflow on Windows and macOS is now "identical" (modulo a few VS Code key binding differences between platforms)!
Polylith & Swappable Implementations
I talked about our plan to implement an http-client
component in part 6 and
that work has been completed and merged in (and is currently in QA). Overall,
it was fairly straightforward to leverage
Polylith's "profiles"
and have two implementations of the same "interface" but integrating it into
our hybrid Polylith/legacy repo did have a couple of head-scratching moments.
We have http-client-hato
and http-client-httpkit
, both implementing the
http-client
interface. We've added :+default
and :+httpkit
aliases to
our main deps.edn
file -- following the Polylith profile naming convention --
so we are using the Hato implementation
by default for development/testing and for nearly all of our Polylith projects
.
Our legacy apps use the httpkit
implementation. It's just a matter of which :local/root
dependency you put
in your deps.edn
file, so that's pretty slick.
You can run tests against either implementation:
poly test :dev
uses the default, Hato-based, profile (the:+default
alias),poly test :dev +httpkit
uses the httpkit-based profile (the:+httpkit
alias).
Project-based tests use whichever :local/root
dependency is declared in their
deps.edn
-- we decided to keep one of them using the httpkit
implementation
for now, just as a sanity check within the Polylith world.
Our hybrid repo also has :everything
and :runner
aliases which mirror
Polylith's :dev
and :test
aliases and we have added the Hato-based component
to the :runner
alias so that our legacy test suite also uses Hato (and the
JDK11+ HttpClient
).
What was head-scratching? Initially, I forgot to also add the Hato-based
component's test
path to the :runner
alias and just got a mysterious
Namespace cannot be loaded
error from the Clojure CLI claiming that our
wrapper namespace for Cognitect's test-runner
could not be found. Since the
"exec" functionality doesn't expose the underlying exception, that took a bit
of debugging.
In addition, Polylith currently doesn't support :local/root
components
in profiles, only :extra-paths
(see issue #146),
so getting the profiles working was not as straightforward as I had expected.
And, finally, actually developing the alternate implementations was a bit
frustrating since I was trying to develop them side-by-side, rather than
creating a new implementation against an existing interface so I was
evaluating both components into the same REPL -- and they both had the
same namespaces (ws.http-client.interface
and ws.http-client.impl
).
After a while, I got into a good rhythm but it took a few attempts before
I settled into "eval .impl
, eval .interface
, eval other code and test,
switch to the other component and repeat". I learned a lot about both
httpkit and Java's HttpClient
while I was doing this, especially around
how they handle async operations, thread pools, executors, and also how
they handle different SSL setups!
Overall, I would consider this a big win for Polylith: our shared code is written against a functional interface, we can easily run all the tests against either implementation with just a command-line flag, and our projects can decide which implementation to use at build time!
Polylith 0.2.13-alpha
And finally, on to the first item I mentioned: Polylith's new alpha release.
The main new feature is a very nice, interactive shell
command that
provides auto-complete and history so you can just run poly
and then
keep that open and work with Polylith without any startup overhead.
You can type i<TAB>l<TAB>
and get the info :loc
command. It also knows
how to offer auto-complete for your bricks and projects so you save a lot
of typing, and can easily scroll back and forth through your history
with the arrow keys.
One of the first things I did when I started working with the poly
tool
was to write a primitive interactive wrapper so I didn't have to keep
running the entire process from scratch every time so I'm very pleased
to see this slick interactive mode in the core tool!
The second "big feature" is that the monolithic README for the tool
has been moved to the poly
GitBook
so it is much easier to navigate and much more pleasant to read. I think
this will be a big help for newcomers to Polylith: there's a huge amount
of documentation and a lot to learn before you can really become effective
with Polylith and its command-line tool and this is so much more digestible.
The final "big" change in this new release is that the project has switched
from a custom build/deploy pipeline, based on a mixture of depstar
and Polylith's
own deployer-cli
base and deployer
component to using tools.build
,
via my build-clj
wrapper library.
This helped iron out a couple of wrinkles in how monorepo-based artifacts
get built so build-clj
now has a :transitive
option for working with
:local/root
components, such as Polylith relies heavily on.
This has also allowed for all build documentation
to recommend using tools.build
to create artifacts from Polylith repos,
instead of relying on depstar
, which makes me happy.
See the 0.2.13-alpha release notes for all the other changes in this release.
If you have a monorepo, or you are contemplating switching to a monorepo, consider Polylith very seriously.