Pavlov for the Web

One of the aims of pavlov is to give developers and LLMs a model-check-driven development workflow that is superior to both traditional TDD and REPL-driven development. When using pavlov, you should be able to use a model to guarantee that your application does nothing you do not want it to do, and everything you do want it to do. This is straightforward to do with an application that runs on a single system. Model checking comes for free with the behavioral programming paradigm. ...

2026-04-17 · 4 min · 698 words · Thomas Cothran

Avoid with-redefs in tests

Advice to avoid with-redefs often focuses on the wrong things. It is true that with-redefs can cause race conditions in your test suite. And that functions can be captured in closures, leading to them not being redefined as one might expect. And that with-redefs doesn’t redefine inlined functions. And that with-redefs can cause problems with type-hinted functions. And that they cause problems when used with macros. And that mutating the global environment is something functional programmers should know is a bad practice. Etc., etc. ...

2025-04-07 · 3 min · 591 words · Thomas Cothran

Top-Down Imperative Clojure Architectures

When I first became interested in functional programming, a more experienced engineer told me: “you know functional programming doesn’t really amount to much more than procedural programming.” As I insisted on the benefits of map, filter and reduce, he simply shook his head. “You’re thinking in the small. Go look at a large real-world application.” It took some time for me to see what he meant. My preferred language, Clojure, is a functional language. But too often it is used to build top-down, imperative applications. This negates the value proposition of functional programming: isolating side effects, local reasoning, and system composition. ...

2024-07-22 · 7 min · 1280 words · Thomas Cothran

MPAs vs SPAs: The False Dichotomy

This is the second post in the series “Have Clojure UIs Taken the Wrong Path?”. The first post is here. How do we choose between building an application using a hypermedia approach versus the client-side SPA? This post won’t answer that question. But it will say how not to make that choice. A common heuristic is the spectrum that stretches between old fashioned “Multi Page Apps” (MPAs) for basic use cases, to Single Page Applications for rich client interactivity. ...

2024-01-28 · 9 min · 1818 words · Thomas Cothran

Have Clojure UIs Taken the Wrong Path? Part 1

The Clojure community has focused on React-based solutions for complex front-end clients such as Reagent, Rum, Om, Re-frame, and Fulcro. For all their differences, they follow a very similar architecture, making heavy use of client-side state and using RPC for client-server communication. We will call this the “React+” approach. But is this the right choice? I will suggest the answer may be negative. My suspicion is that as web UI libraries advance, the problems they solve are not essential. Rather, the problems are accidental; they are generated by the React+ architecture. ...

2023-11-24 · 9 min · 1780 words · Thomas Cothran

The Wrong Kind of Readability

In the Pragmatic Programmer, Andy Hunt and Dave Thomas tell us: “it’s critical that you write code that is readable and easy to reason about.” This seems uncontroversial; it is the rare point on which software engineers typically agree. Or do they? In fact, developers disagree about what “readability” means. “Readability” can be given two contrary meanings that we will call imperative readability (or readability-i) and declarative-readability (or readability-d). Imperative Readability This form of readability focuses on how the code operates. It emphasizes a clear presentation of the implementation details, such as which libraries are used, how data sources are accessed, and how functions perform their tasks. ...

2023-11-05 · 10 min · 2038 words · Thomas Cothran

Brittle Clojure: Creating Legacy Clojure Systems

This is the first in a multi-part series, “Brittle Clojure”. In this series, we will consider common patterns in Clojure which yield brittle systems, as well as methods to ensure robustness. None of the basic principles for building robust software are unique. Most literature, however, is focused on object-oriented systems. Our point of view will sometimes zoom in to Clojure “in the small”, and sometimes zoom out to distributed systems built with Clojure. ...

2023-07-23 · 11 min · 2187 words · Thomas Cothran