Holy Dev Newsletter July 2023
Welcome to the Holy Dev newsletter, which brings you gems I found on the web, updates from my blog, and a few scattered thoughts. You can get the next one into your mailbox if you subscribe.
What is happening
I have written two blog posts. The first one is my highlights from the excellent book Escaping the Build Trap. Taster: "It doesn’t matter how good you are at making software, if you are building the wrong thing. Melissa Perri’s Escaping the Build Trap is an excellent book about fostering culture focused on customer’s problems and producing value." Highly recommended!
The other blogpost is When not to use Fulcro?: "Fulcro is great for writing non-trivial, full-stack business SPA web applications, which display and modify data from a general data store. But it can’t possibly be perfect for every kind of web app. So when is it less then a perfect fit? Possibly still usable, but not as beneficial?"
The biggest event of the month for me has been my London Clojurians talk Why you need Fulcro, the web framework to build apps better, faster (slides). I think it went pretty well and I got some positive feedback.
I have finally finished the Fulcro Cookbook recipe Solving mutually recursive elements with lazy loading with hooks and dynamic components, so have a look! Many thanks to Tony and Aram, who were instrumental in making it happen. (Gene Kim inspired me to add a live demo of the app, which I haven’t yet gotten to.)
Other than that, I got some time to read, as you can see in the Gems section below :-). My favorite article was Thomas Heller’s The Lost Arts of CLJS Frontend, reminding us that we can use browser APIs directly, and that we can built anything from super-lightweight cljs frontends ~ htmx to library-heavy SPAs. The thing I haven’t read yet, which I am most looking forward to studying, is the Recife Guide (Recife if Clojure wrapper for TLA+, a formal method language for specifying systems with temporal properties and verifying that their behavior is correct.)
The thing I am most excited about is Red Planet Labs coming out of the stealth-mode with their 100x productivity programming language/platform in couple of days. (I’ve have written about RPL’s efforts before.)
One more thing - I haven’t mentioned previously that I maintain my Fulcro Field Notes and similarly, Fulcro RAD Field Notes, which are collections of observations from my practice and from Slack. In the former one, there is a new note on when to use React hooks in Fulcro. (See also the Cookbook’s recipes' insights section for why using hooks is not ideal.)
Other Fulcro highlights
Gene Kim (yes, the one) has published a series of recordings pair-programming sessions between himself and Tony Kay (and sometimes myself), as he builds a couple of Fulcro RAD-based applications. He came to Fulcro as a true beginner, so everyone can learn something from these. (You can find it listed at Awesome Fulcro.) Bunch of these have pretty detailed descriptions, which helps you find the parts interesting to you. You will learn a lot about design both in general, and in the context of Fulcro.
Gems from the world wide web
👓 Ministry of Testing - The Biggest and Most Supportive Global Software Testing Community [testing, community]
Reportedly a great community for QA folks. Also, great name!
👓 thheller/shadow-css: CSS-in-CLJ(S) [library, clojure, webdev] - CSS-in-CLJ(S)
shadow.css is essentially a mini DSL for writing CSS directly in Clojure(Script) code, allowing you to directly write CSS where it is used. Rationale: Remove the burden of switching between code and .css and inventing names to bind them together. Using Tailwind CSS is a great alternative, but requires additional JS tooling. Gaols: 0 runtime and code size impact, expressivity of Tailwind with access to all of CSS, no extra tooling, integrates seamlessly with external CSS. Its css macro accepts CSS DSL and returns a css class name: {:className (css :px-4 :shadow {:color "red"})}. (Top-level keywords are mostly same as tailwind aliases, maps are raw css property (as kwd) + value pairs; numeric values represent Tailwind spacing scale (1 = 0.25rem, 2 = 0.5, ...), kwd values are aliases (to Tailwind's or your own).) You can also include selectors to target sub- or pseudo- elements, and more.
👓 AI image generation for teams - You can easily generate AI logo, AI book covers, AI posters and more - Stockimg AI [ai, service, writing]
Generate stock images & more with AI.
👓 Babashka Conf 2023 - YouTube [clojure, talk]
👓 Clojure Guides: Building Projects: tools.build and the Clojure CLI [clojure, learning]
By Sean Corfield: The whole thing is over 3,000 words now, with a lot of code examples. I’ve tried to distill everything I’ve learned about tools.build into a single document that covers various scenarios that go beyond what is in the official tools.build guide.
👓 Mutex without lock, Queue without push: cancel safety in lilos - Cliffle [rust, async]
An interesting discussion of the concept of "cancel safety", i.e. Future operations that can be cancelled without any ill effects, such as data loss, corrupt data etc. It defines levels of cancel safety and gives two examples of rewriting functions in lilos, the tiny async OS for microcontrollers, to be strictly cancel safe.
👓 gnl/playback: #>(tag'n'trace any Clojure(-Script) form to tap> and Portal with automatic last-input function replay on... [clojure, tool, troubleshooting] - The "love child of debux and Portal" gives you 2.5 reader macros to easily trace your code, i.e. watch data flowing through using Portal, as a replacement for println statements. Moreover, you can refer easily to data currently selected in Portal (e.g. to feed them to expressions), and Playback stores the last input to traced functions and re-runs them with it, when they are re-evaluated. So you change and eval your fn, and see the data flow update in Portal, for instant feedback.
👓 soulspace-org/overarch: Overarch provides architecture descriptions as data based on the C4 model, opening multiple use... [clojure, tool, documentation]
If you want to document your architecture with the C4 model, you might do it using EDN rather than non-composable text files.
👓 How to Hire Clojure Developers [clojure, HR]
Some good tips for what to check when hiring a Clojure dev.
👓 JUXT Blog: Day-to-Day Event Driven Architecture [architecture]
The first post in a longer series about Event Driven Architectures (event sourcing, etc.). Only two parts so far, focusing on an overivew. Might be a good reference in the future.
👓 quoll/tiara: A small data structure library [clojure, library]
A data structures library by Asami's Paula Gearon, currently only including an ordered map and an ordered set. O(1) access, in worst case O(n) disj/dissoc.
👓 Clojure Multi-stage Dockerfile - Practicalli Engineering Playbook [clojure, devops]
A good, detailed guide for building Docker images with Clojure uberjars, leveraging multi-stage docker build so that the build also happens in a well-defined Docker environment.
👓 talk-transcripts/Hickey_Rich/DesignInPractice.md at master · matthiasn/talk-transcripts [clojure, talk, design]
A transcript from Rich Hickey's insightful talk about the practice of software design from Conj 2023. Link to the video included.
👓 NikolaySuslov/krestianstvo-electric: Krestianstvo | Electric - Clojure. Implementing a scalable Croquet VM. [clojure] - Krestianstvo | Electric - Clojure. Implementing a scalable Croquet VM. - GitHub - NikolaySuslov/krestianstvo-electric: Krestianstvo | Electr
I am not really sure what this is 😅 but the ideas and libs it uses are worth studying - Virtual World Framework for building collaborative, immersive applications in the browser (realtime state synchronization, 3D, A/V) and Croquet, an SDK for use in developing collaborative virtual world applications (reportedly known for its radical synchronization system -> What is Croquet Anyways) and, of course, Electric Clojure. Just leaving it here for long winter nights...
👓 Chris James - HTMX is the Future [webdev, opinion]
HTMX is for building server-driven webapps, where frontend is sprinkled with few tags to fetch and insert fragments of HTML via Ajax - contrary to the frontend-heavy SPAs. This (biased) article explores its advantages. "All HTMX does, is make the browser better at hypermedia by giving us more options regarding what can trigger an HTTP request and allowing us to update a part of the page rather than a full page reload."
Pitch: Get rid of the complexity of Single-Page Apps, and move development back to backend. Discusses the cost of SPAs (complexity, changing tooling, managing state on both FE and BE, etc.). HTMX embraces the architectural approach of REST (of returning self-describing hypermedia to thin clients) and "it augments the browser, improving its hypermedia capabilities and making it simpler to deliver a rich client experience without having to write much JavaScript if any at all." A bunch more of great points (the cost of FE x BE divide, fast moving FE landscape, duplication of biz/authx logic, etc.).
Recommended to pair with Thomas Heller's critique of HTMX in his The Lost Arts of CLJS Frontend. His main point is that the elementary approach HTMX uses (adding to HTML anotations that JS hooks into) is nothing new, and has a well known problem - scaling. Namely, the library provides a set of functionality when (not if!) you need something slightly more or different, you need to work around it, becoming more and more complex. Thomas argues for a middle ground: lightweight Cljs frontends. (You don't always need SPAs!) I'll leave with this pitch: "On the surface this is exactly what HTMX does, however I want to present how to do this in CLJS in a scalable way, which takes you from tiny snippets of functionality to full-blown SPA if needed." (Side note: Biff demoes how to enhance htmx with e.g. Electric.)
More highlights: "[..] the amount you have to learn to be effective [with React] is unreasonable for most applications".
👓 mockoon/mockoon: Mockoon is the easiest and quickest way to run mock APIs locally. No remote deployment, no account... [webdev, tool]
I haven't tried this, as writing a simple mock API in Clojure is enough for me, but it could be useful to someone. One of 20 projects in GitHub Accelerator 2023. About: "It's a desktop application and a CLI that help you work faster with APIs by mocking them. Integrate third-party APIs quicker, improve your integration tests, speed up your development, etc."
👓 formbricks/formbricks: Open Source Surveys & Experience Management Solution [webdev, tool, SaaS]
[Self-]Hosted (in-app, I believe) mini-sureys to gather data from your users for data-driven product decisions. One of 20 projects in GitHub Accelerator 2023.
👓 The Lost Arts of CLJS Frontend [webdev, opinion, clojure]
Thomas looks at the recent craze around HTMX, discusses its limitations, and shows us how to take control and build as simple or as complex apps on the HTMX - SPA scale as we need to. Refreshing!
The problem with HTMX is that it doesn't scale, because it provides a set of functionality and when you need something slightly more or different, you start running into problems.
Instead, you can take essentially building DIY HTMX using pure JS and interop as a starting point, and add libraries and utilities as your needs grow. Here you can make it as convenient as HTMX, but you retain full control. Thomas then demonstrates how to use e.g. re-frame for a tiny part of the UI, which needs more FE behavior.
Thomas mentions that "Forms are probably one of the more complex things in web development. You might want to add client side validation, dynamic fields, image uploads or whatever else." Thus I guess this is one place where HTMX would become too limited.
Also check out the follow up posts Applying the Art of CLJS Frontend (demo of rewriting Biff's eelchat from htmx+hyperscript to DIY lightweight cljs with shadow-graft ) and Mastering the Art of CLJS Frontend (digging into working with DOM from Cljs).
Aide: shadow-graft is a small lib for integrating FE and BE code: define multimethods for "progressive enhancement" on FE (taking BE-supplied data and a DOM element), and "call" them when constructing the html on backend, targetting them at closeby elements: [:a ...] (graft "ask-before-navigating" :prev-sibling {:my-data nil}). Thus FE defines reusable pieces of "progressive enhancements", while BE ddecides where to apply them - i.e. the information what needs to be enhanced is collocated with the target DOM, instead of being spread far and wide.
👓 Datomic Cloud multi-tenancy [clojure, architecture, datomic]
Approaches to, considerations, and trade-offs related to implementing multi-tenancy in Datomic Cloud.
By database - minimal code and strong data isolation x limited scalability (up to 100s perhaps, depending on the concrete case and sizes?). You could distribute the DBs over multiple Datomic Systems, for a greater cost. "In cases where we expect thousands of tenants, such a B2C apps, the cost element becomes unreasonable."
Shared database & DIY data separation - e.g. add [entity, tenant] for each entity & include in queries (possibly wrap Datomic API with one adding these checks). More difficult with GDPR (since we can't simpli delete a DB, and Cloud lacks excision).
👓 Choosing a Direction for Datomic Ref Types [datomic, performance] - Datomic Reference attributes associate two entities together, but also have a required direction. Which direction should you choose?
Rule of thumb: for hgih-cardinality one
👓 Calva, Joyride, and Portal [clojure, productivity]
Sean's tuned melange of Calva, Joyride, and Portal. Uses reflector to navigate to source or docs for a var, dev.datafy, jedi-time for datafication of java.time, dbxray to generate specs from JDBC DB. Nice hack: his .clojure/deps.edn GH repo is also a git library, with repl enhancement code. Two Portal windows, one for explicit tap> and one for all code evals, and logging. Remote debugging with Portal (hooked into tapss and logs) through a ssh tunnel, with a single shortcut. 🤯
👓 clj-commons/gloss: speaks in bytes, so you don't have to [clojure, library, networking]
Gloss is a byte-format DSL. It can turn complicated byte formats into Clojure data structures, allowing for easy use of custom network protocols and C libraries. It can also turn Clojure data structures into compact byte representations, allowing for efficient use of bandwidth and disk. See its intro docs.
👓 zalky/cues: Queues on cue: low-latency persistent blocking queues, processors, and graphs via Chronicle Queue [clojure, library]
Low-latency persistent blocking queues, processors, and graphs via Chronicle Queue. For when distributed systems like Kafka are too much, durable-queue is not enough, and both are too slow. Goal: a minimal DSL for connecting message processors into graphs using persistent queues in a non-distributed environment.
Chronicle Queue is a broker-less queue framework that provides microsecond latencies (sometimes less) when persisting data to disk. Not distributed.
The processors and graphs are meant to provide a dead-simple version of the abstractions you get in a distributed messaging system like Kafka.
Usage: E.g. robust, persistent, low-latency communication between threads or processes, or anywhere you might use clojure.core.async but require a persistent model.
👓 strojure/web-security: Decoupled web security implementations for Clojure. [clojure, library, security]
Decoupled web security implementations for Clojure. - Content Security Policy, HSTS, Referrer-Policy.
👓 Recife Guide: Learn TLA+ for system specification, with Clojure [clojure, formal methods, learning]
Paulo Feodrippe has created Recife, to make the TLA+ formal specification language + checker more accessible to Clojure devs. Now he also started Recife Guide to teach it. Check out the short example from its quick start, of a small model where TLA+/TLC found a design error.
👓 Exception Translation [clojure, troubleshooting, productivity] - Can a typical “Internal Server Error” be improved a bit? Let’s find out.
In fact, let’s consider more than just a response that is typically
A great article. Do not lump all unexpected errors at the top level together into a single, generic 500 "Internal Error". Look at what kind of exceptions occur, distinguish the truly unexpected (likely bugs) from transient network errors and high load problems. Translate the exception into something more useful, e.g. by adding a more specific error code and message for a given exc. category. The messages should be good enough for other developers, QA, maybe other employees, or service-to-service communication, likely not end-users. Adjust accordingly for them. (Note: cognitect's anomalies lib defines a couple of common error categories, among others.)
👓 Integration Tests for SQL Statement and Lock Timeout [testing, database] - If you’re using (and have configured) SQL statement and lock timeout, it would be good to verify how the service behaves when those timeouts
Another excellent blog post from my colleague Miro, this time demonstrating how to trigger a statement or lock timeout in Postgres so that you can test your app's reaction. For stmts, leverage SET LOCAL statement_timeout = '10ms' and SELECT pg_sleep(0.1). The lock test fires two concurrent transactions, synchronized via Java's CyclicBarrier and CountDownLatch to ensure that they first try to modify the same row and then enable the test to finish. Well done!
👓 Code Observation: Function Arity in Clojure [clojure]
A deep look at how and why multiple arities are used in clojure.core, i.e. different categories of reasons for duing so. Beware, long read! Might be useful to "tease out useful patterns so that our own functions compose ergonomically with those of core Clojure and the greater ecosystem."
👓 JUXT Blog: API development, quickly and securely [clojure, library]
Introducing Juxt's Site, for creating APIs quickly so that teams can start iterating faster. Site allows us to upload API definitions in OpenAPI format and will implement the API dynamically [reading/writing XTDB], as if the back-end code had been generated directly from the API specification. Also supports GraphQL in addition to OpenAPI. Built-in, non-trivial authorization.
When learning Clojure is hard - tip
By eigenhombre at ClojureVerse
axllent/mailpit - mail server for testing in CI
👓 axllent/mailpit - mail server for testing in CI [testing, tool]
Acts as a mail server (SMTP or API) for testing emails from your code. Static binary so can easily be run in CI/CD. API allows querying of received emails so you can check they were correctly “sent”. Can persist mail to a SQLite database. Has a web UI to inspect mail e.g. for local development. Via Console.dev
--
Thank you for reading!