Book recommender
Pick a book you liked and get ten more. It learned from 280,669 interactions of readers with 2,405 books in the Book-Crossing data. I built the first version by hand in 2025 and the second with Claude Code in 2026: I set the goals, reviewed every change and checked every number. Switch between them to see the difference.
The code on GitHub (opens in a new tab)How v2 was measured (opens in a new tab)v1’s report (PDF) (opens in a new tab)
Try it
Loading 2,405 books…
How well it recommends
Every version was tested the same way. A fifth of each of 2,271 readers’ ratings was held back; a version scores a hit when one of the ten books it recommends to a reader is a held-back book they rated 4 or more out of 10. NDCG also rewards ranking that book higher. v2 finds one for 48.4% of readers.
| Version | Readers with a hit | NDCG@10 | Books ever recommended |
|---|---|---|---|
| Most popular books | 16.3% | 0.033 | 2.1% |
| v1 as shipped (user-based CF) | 28.3% | 0.087 | 82.0% |
| v1's API table (dot product) | 30.7% | 0.088 | 19.3% |
| Stage 2: cosine table, clean books | 28.0% | 0.092 | 70.2% |
| Stage 3: EASE, all interactions | 46.5% | 0.162 | 54.8% |
| v2: blend of EASE, RP3beta and text | 48.4% | 0.173 | 60.0% |
How it was built
v1 · 2025 · by hand
Exploratory analysis of the data; user- and item-based collaborative filtering and a content-based model, in notebooks; a FastAPI service on Fly.io; a 30-page report. Kept exactly as it was, at tag v1.0 (opens in a new tab).
v2 · 2026 · with Claude Code
1. Measure
Re-ran v1 from its own code and got every number it had published. Its rating errors came from an averaging bug, while its user-based model, as shipped, ranked books best of its models.
The report (opens in a new tab)2. Clean
v1 told books apart by their exact title, so editions were different books and different books with one title were one. Identified properly, its 2,508 books are 2,405, each with its details from Open Library.
The report (opens in a new tab)3. Model
Trained on six times the data, books readers had without rating them included, and tuned EASE, RP3beta and a text model on ratings set aside for it. Their blend doubles v1's NDCG.
The report (opens in a new tab)4. Show
This page, with no server: each book's ten are worked out ahead of time, from the same model.
Ratings: the Book-Crossing data (Cai-Nicolas Ziegler, 2004), from Kaggle’s Book Recommendation Dataset. Book details and covers: Open Library (opens in a new tab).
v1’s lists are what its API returned, shown with v2’s cleaned titles. Each list shows one edition of a book. v2 never recommends Wild Animus: its publisher gave copies away through BookCrossing by the thousand, so having it says who received one, not who chose it, and it would top every list.