Biff.core: system composition for Clojure web apps

jacobobryant3 pts0 comments

New library: biff.coreBiff 2 is in development. Try it out.

HomeDocsNewsGitHub

New library: biff.core<br>Jacob O'Bryant | 9 Jun 2026

As I wrote about previously, I've been working<br>on splitting Biff up into a bunch of separate libraries and changing various<br>things along the way. I've completed a rough draft of all twelve<br>libraries and am now<br>going through them one-by-one to polish and release them. The first library is<br>now ready.

biff.core: system<br>composition and other interfaces for Biff projects. This is the glue that holds<br>all the other libraries together, and that's why I'm releasing it first.

For a long time Biff has had this "modules and components" structure where each<br>application namespace in your project exposes a "module" map, then you have a<br>bunch of boilerplate to combine stuff from those modules into a single "system"<br>map, and then we thread the system map through your "component" functions on<br>startup. Biff 2 retains that structure, and it has some additional stuff to deal<br>with that boilerplate.

For an example of what I'm talking about, see this<br>code<br>which takes the :routes (and :api-routes) keys from your modules and turns<br>them into a :biff/handler value for the system map. I wanted a first-class way<br>to be able to extract that kind of logic cleanly into a library so that the<br>library's instructions can just be "add this module to your project" without an<br>accompanying "and then paste all this stuff into your main namespace."

So this new biff.core library includes a concept of "init functions." These are<br>functions that take a collection of modules and return a single map that can be<br>merged into your system map. Ta da. Here's an<br>example.<br>Init functions are stored in the :biff.core/init key in your module maps, so<br>we get that nice "all you need are modules (well, and components)" effect.

The main complication here is that the boilerplate of defining a (def handler ...) var in your application code actually has a nice side benefit: late<br>binding. If you change any of your modules, the handler var will get updated,<br>and if you set :biff/handler in your system map to the var instead of the<br>value (#’handler), incoming Ring requests get the latest handler without you<br>having to restart the web server. If we extract that boilerplate into library<br>code, we don't get the var.

I ended up on this solution:

Init functions take a var of your modules vector, not the vector value<br>itself.

Anything in the system map that you want to get updated without a restart<br>needs to be a function. In some cases this means instead of setting a<br>:com.example/my-thing key on the system map, you need to set a<br>:com.example/get-my-thing function which returns my-thing.

That function on the system map should dereference the modules var and pass it<br>to a memoized function that builds whatever thing it is you need (like the<br>Ring handler).

Again, see this<br>example.<br>The result is kind of aesthetically pleasing: you get a nice clean main<br>namespace<br>that shouldn't need to change much, and all you do is add<br>modules<br>and<br>components.

There's always the temptation to consolidate things further. Why even have a<br>separate components vector? Why not have modules support :biff.core/on-start<br>and :biff.core/on-stop keys and then have some way to express dependencies<br>between these lifecycle functions so we can call them in the right order?

And the answer is so that we don't have to have some way to express dependencies<br>between these lifecycle functions so we can call them in the right order. It's<br>not that hard to put the components in the right order yourself (especially<br>since the Biff starter project does that for you), and then it's easier to<br>understand how components work. It's just a sequence of functions that you pass<br>a map through. If you work on a project with so many stateful resources that<br>it's hard to keep track of them all, you can always layer something on top that<br>figures out what your components vector should be before you pass it to<br>biff.core.

Plug: my team is<br>hiring<br>for a senior software engineer, writing ClojureScript and Python mostly. We make modeling software<br>for renewable energy projects.

Sign up for Biff: The Newsletter<br>Announcements, blog posts, et cetera et cetera.

Subscribe<br>It looks like that email is invalid. Try a different one.<br>reCAPTCHA check failed. Try again.<br>There was an unexpected error. Try again.

RSS feed · Archive

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

biff system modules core functions library

Related Articles