biff.fx: lightweight effects systemBiff 2 is in development. Try it out.
HomeDocsNewsGitHub
biff.fx: lightweight effects system<br>Jacob O'Bryant | 16 Jun 2026
I'm releasing another Biff 2 library:<br>biff.fx. It's a<br>lightweight approach to removing effects from your application logic, which<br>makes that logic easier to understand, test, and reuse.
There are basically two ideas here. First is the common approach of having your<br>code return data describing effects (http requests, database<br>queries/transactions, etc) it wants to run instead of running those effects<br>directly. So for example, instead of calling (http/request "https://example.com" {:query-params {:foo "bar"}}), you would return a vector<br>like [:my-application.fx/http "https://example.com" {:query-params {:foo "bar"}}], and then some sort of orchestrator would call http/request for you.<br>Then it's easy to unit-test your code since it's pure, and if you wanted to swap<br>out certain effect implementations when running integration tests, that's easy<br>to do too.
You could set something like that up with Ring middleware where effects run<br>before and after the handler. Handler functions could somehow declare what input<br>data they need/what database query they need to run, if any, and then they could<br>return some effect data for any database transactions etc they need to do<br>afterward.
That works as long as you can structure your logic and effects like a sandwich,<br>with effects on the outside and gooey, pure logic on the inside. What if you<br>need logic on either side of an effect though, i.e. what if you need to<br>interleave logic and effects? For example, in one of my apps I have some code to<br>initialize a Stripe checkout session. It has to:
Query our database to see if the current user has a Stripe customer ID
If not: hit the Stripe API to create a new customer and save the returned ID<br>in our database
Hit the Stripe API to create a new checkout session
Save the session ID in our database and redirect to a URL returned by the<br>Stripe API
The database bits can be pushed before and after our logic, however the HTTP<br>requests can't. So what do we do?
One approach would be to just have a special case for situations like this under<br>the assumption that most of your code can in fact be structured as a sandwich.<br>i.e. write a plain-old-impure-function and be on with your day.
Another approach is to use data not just to describe effects but also to<br>describe control flow. One of the shapes that approach can take is:
Each "chunk" of logic from a previously impure function is extracted into a<br>separate, pure function.
Those functions can return data that describes both (1) what effects they need<br>to run, and (2) which pure logic function should run next.
i.e. you make a state machine where the states are pure logic and effects happen<br>in the transitions. And that's what<br>biff.fx does.
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.