Lectr

Your reading companion

March 2026

Being Boring

Lectr’s recommendation feature needed a way to avoid suggesting books you’ve already seen. My first solution was a Bloom filter. The client maintained the filter locally and sent it with each request. The server checked candidates against it before returning results.

I liked this. Bloom filters are probabilistic in exactly the right direction. False positives (skipping a valid recommendation) are harmless, and false negatives don’t happen. It felt like the right data structure for the job.

It was also unnecessary.

The simpler approach: overfetch on the server and filter on the client. The server returns more candidates than needed, the client drops any it’s already shown, and the user sees what’s left. No shared state between client and server, no filter serialisation. The request shrinks by ~17 KB (the base64-encoded Bloom filter) and the response grows by ~2 KB (a few extra recommendations). Net saving of ~15 KB per request, and the saving is on the upload, which is the expensive direction on cellular.

The Bloom filter solved the problem correctly. The overfetch approach solves it adequately, with less code and fewer things to think about. I removed the Bloom filter and shipped the boring version.

The hardest part was deleting something I loved the elegance of.

Lectr is available on the App Store for iOS. One-time purchase, no subscription. Android - coming April 2026.

Download on the App Store