Biff 2.0 is released

Jacob O'Bryant | 10 Sep 2026

Last April I outlined some changes I had planned for Biff, which included making SQLite the default database, splitting the monolithic com.biffweb namespace into a bunch of independent libraries, using Datastar by default, introducing some new approaches for keeping large codebases maintainable, blah blah blah, and bumping the version to 2.0.0. I'm pleased and slightly exhausted to announce that those changes are SHIPPED and you can try them out like this:

git clone https://github.com/jacobobryant/biff-starter my-project
cd my-project
clj -M:run dev

I have tweaked the landing page, written the documentation, and even started, just barely, to actually use Biff 2 to make a new app.

If you've used Biff 1, then please be advised that there are technically no breaking changes (since everything is in new namespaces) and that I've written up some guidance on gradually introducing Biff 2 into a Biff 1 codebase, if you so desire. There are some breaking changes if you've already been trying out the Biff 2 prereleases.

A few things from Biff 2 that I find particularly interesting, some of which are covered in more detail by the aforementioned blog post:

  • This demo.clj file from the starter project gives a short yet representative taste of what application code in a Biff project actually feels like. Note the parameters injected into demo-page by biff.graph; the POST request handlers that return their side effects as data via biff.fx; the fact that only a single handler needs to return HTML and those POST request handlers don't need to concern themselves with rendering at all, and yet the page is fully reactive--thanks to Datastar.

  • The starter project is now a standalone repo and can be easily forked and modified if you want to create an alternative starter project (with, say, a different database).

  • Speaking of using different databases, there is a guide on writing a database adapter. This makes switching out the default database much easier than it was in Biff 1 (no need to rewrite the authentication module, for example).

  • biff.core's new module system. The flip side of making Biff more modular is that there's an increased need to have well-defined interfaces for the modular pieces to plug into. I decided to extract Biff's "framework" logic into a library not just to reduce boilerplate but also to ensure things are being done the way that Biff 2 libraries expect.

  • defpipeline, a very recent addition to biff.fx that cuts down the boilerplate and IMO makes using biff.fx feel pretty ergonomic.

  • biff.graph of course. The whole thing.

  • biff.run and biff.tasks, the latter of which has a video demo of using the prod-setup and deploy tasks to deploy a vanilla (non-Biff) Clojure app to a fresh VPS.

My overall thoughts on where Biff has ended up: I'm definitely taking some bets here. biff.fx and biff.graph are a bit weird. Awesome, but weird. Will the benefits really matter for the projects people use Biff for? biff.core's module system, despite being fairly lightweight, is still not as lightweight as the 5-line reduce call that Biff 1 used. Does that similarly push Biff further out of good-for-a-weekend-project territory? At the same time as I'm adding these features to benefit large codebases, does switching to SQLite--an embedded database--make Biff less attractive for projects that are likely to end up with large codebases?

My north star is still "what do I want for myself," so despite the hypotheticals above, I'm not actually that concerned: I think all this new stuff is ridiculously sweet. And I do think the modularity and the related ease of making alternative starter projects is somewhat huge. Biff is much more evolvable now. If I've gotten anything wrong, it really shouldn't be that difficult for anyone (including my future self) to fork my starter project and fix whatever it is that needs fixing.

Except for biff.core; we're stuck with that part now.

Sign up for Biff: The Newsletter
Announcements, blog posts, et cetera et cetera.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.