How Graze built a "composable personalization" engine for the open social web — and why it matters right now.
Personalization has been a story told exclusively by platforms for far too long. They decided what “For You” meant, and as a result, they have optimized their timeline and metrics to their own definition of “good.” You were the subject of the algorithm, not its author.
That needs to change, and we believe that you’re going to play an important part. We believe that everyday people need steerable, customizable controls to shape their own social media experience and share that experience with others. The future of social will be open, decentralized, agency-granting, and deeply shaped by the community. That has been one of the central promises of ATProto (and for what it's worth, other major platforms are starting to see the writing on the wall in this direction, likely too late).
Today we're releasing an engine that will make that work dramatically easier within the ATProto ecosystem.
Social Proof
Collaborative filtering and recommendation are deeply human. “People like you also liked this” is how we share stories, recommendations for where to eat, and how we connect. Advertisers call it word of mouth, trendsetters say trusted tastemakers, and you probably have that one friend who always knows the right restaurant. That’s collaborative filtering.
If you haven’t used the “For You” feed, created and maintained by spacecowboy17, we highly recommend you check it out. That feed is a model for what collaborative filtering looks like in the Atmosphere, and has been instrumental in setting the standard that we aim for in our own work.
We’re deeply inspired by what spacecowboy17 has built and believe that all feed creators can and should benefit from the lessons learned by their design. For Bluesky to win, anyone should be able to build a for-you feed of their own.
Our Own Take
We’ve been prototyping and experimenting to implement personalization at the scale Graze operates at. One key feature of the direction we landed on was how we traverse the graph. Instead of starting from the viewing user and traversing outward at query time (from user to liked posts to co-likers and so on), we pre-compute each user’s co-liker weights and cache them. At scoring time, we iterate over candidate content and compare the co-liker membership. This results in a scoring process that runs in linear time over the candidate set, regardless of how deep any individual user’s “like” history goes.
It’s a big sky, and there are a lot of parameters that shape each feed and this algorithm. How many users should be considered for recommendations? How much does each “like” weigh? How many co-likers are required, and is there a ceiling? Do we penalize virality, or lean towards more recent?
We use Thompson Sampling to automatically tune these different parameters. It’s a Bayesian approach to the multi-armed bandit problem. Each parameter runs its own independent bandit, and we’ve found that good outcomes increase the probability, while bad outcomes decrease it. A 10% holdout group lets us compare against baseline defaults, and a small exploration rate keeps us from getting stuck.
One thing that we recognize is that “good” results are subjective. Thompson Sampling optimizes for whatever objective you define, and right now, our success signal is a blend of personalization coverage, candidate richness, and latency. Your definition of success may be different, and this design lets you customize those targets. Long term, we'll need to increase the efficiency of our adaptive sampler, and increase the steerability, but we are firmly at the beginning of that work with this release.
Technical Design
We chose to make this a general-purpose microservice built in Rust that can adapt to whatever you throw at it. Feeds can be configured with relatively simple data structures, and you can implement the data loader in whatever language you want. This design keeps the entire system flexible and lightweight, even as it serves millions of posts per day.
One key architecture and design decision was to use Redis. Every feed has its own pool of eligible posts that can be sourced externally. In our infrastructure, we use ClickHouse with a loader that keeps Redis up to date with the latest, accurate feed content for each feed algorithm.
What We’re Seeing
In our own A/B tests across various feeds across hundreds of thousands of test requests in the last week in conjunction with members of our community, we’ve been seeing a 2–6x increase in positive engagement — likes, reposts, replies — when personalization is active compared to the non-personalized candidate sources our engine draws from. In user surveys, we've found that 80%+ of respondents indicated they'd be likely to use the personalized feed again. Your mileage will vary depending on candidate pools and target audiences, but the signal is consistent and strong: when people see content filtered through their own taste graph, they engage with it meaningfully more.
The system also handles cold start gracefully. New users with few likes get a tiered fallback blend of popular, trending, and discovery content. As they accumulate signal, personalization gradually takes over. By the time someone has a few dozen likes, the feed is running at roughly 80% personalized content — and it feels qualitatively different from the generic version.
The Moment
The timing of all this is not lost on us.
For the first time, Bluesky is seeing stasis in user activity rather than the leaky bucket pattern that has characterized much of its growth period. People are staying. The question now is whether we can switch from stasis to growth — whether the experience can become compelling enough that people not only stick around but bring others with them.
We believe personalization is a critical piece of that puzzle. The open social web has proven it can build great infrastructure, great clients, great communities. What it hasn’t yet proven is that it can deliver the kind of eerily-good, “this feed knows me” experience that keeps people opening an app every day. That’s what we’re trying to build — not by surveilling people or optimizing for time-on-site, but by making the taste graph work for them, transparently, with the knobs in their hands.
We’re still early. We’re still figuring out what’s possible. But this engine — steerable, self-tuning, built to be a commons — feels like real progress toward the feed experience the open social web deserves.
This project is open source under the permissive and flexible MIT license: https://github.com/graze-social/personalization