Thumbnail for Five Lessons I Learned From Building a Cross-Platform Real-Time Notification System

Five Lessons I Learned From Building a Cross-Platform Real-Time Notification System

August 18, 2025

At Foodsocial, I was responsible for building the notifications system end-to-end. When I started, we had no system at all, no way to let our users know when their favorite creators dropped new recipes or meal-plans. A year later, we were serving notifications in real time to more than three million users across web, iOS, and Android.

This post is split into two parts: first, the journey of how the notifications system came to be. Second, the lessons I learned along the way.


The Journey

1. Polling on the web
The very first version was as simple as it gets: the client would poll the backend every few seconds to check if there were new notifications. It was inefficient and a bit ugly, but for our small userbase at the time it was enough to prove the idea. People were getting notified, and that was the win.

2. In-app notifications panel with a counter
Once notifications were useful, we needed a proper place for them. We added a notifications panel inside the app, along with a counter for unseen notifications. This was still backed by polling, but it gave users a central place to check for updates instead of relying on scattered alerts. It also made notifications feel like a real part of the product.

3. Real-time with Web Workers and Web Push API
The next step was to make notifications actually real time. I built a system around Web Workers and the Web Push API so updates showed up instantly, without needing a refresh. On paper this was a big leap forward, but in practice it was a huge challenge. Managing workers, handling state, and debugging odd timing issues turned into some of the hardest work in the project.

4. Cross-browser pain
Web Workers and Push worked fine in Chrome, but Safari was a nightmare and Firefox had its own quirks. Getting consistent behavior across browsers ate up weeks. At some point it became clear there was no escaping browser-specific code paths. Debugging across environments was exhausting, but necessary if we wanted the experience to feel seamless everywhere.

5. Mobile push with APNs and FCM
After the web experience was stable, we expanded to mobile. That meant integrating APNs (Apple Push Notification Service) for iOS and FCM (Firebase Cloud Messaging) for Android. Each platform came with its own rulebook around permissions, background behavior, and delivery guarantees. Making sure everything synced with the in-app panel was another layer of complexity, but this step turned the notifications system into something real across all platforms.

6. Scaling with custom pub-sub
The last piece was scaling. Once FoodSocial hit millions of users, polling and ad-hoc delivery weren’t going to cut it. We needed a proper pub-sub system at the core. After testing options, we ended up building our own custom pub-sub tailored to our workload. This gave us the reliability and throughput needed to serve notifications at scale without the system falling apart.


Lessons Learned

1. Start small and validate quickly
The first hacky version with polling was the most important. It showed us that notifications mattered enough to invest in.

2. Don’t underestimate real-time
Moving from polling to push seems straightforward on paper. In practice, real-time systems introduce a whole new layer of complexity.

3. Safari is not your friend
Safari alone can eat weeks of engineering time. Testing on every browser is not optional.

4. Mobile push is its own beast
APNs and FCM are worlds apart. Permissions, background handling, and syncing across devices are all tricky problems you only discover once you go live.

5. Logs and consistency are everything
Delivering a push notification is only half the job. The harder part is keeping the state consistent across web, iOS, and Android so the user experience feels seamless. This is where logs became essential. Without detailed logs, it’s impossible to debug why a notification showed up on one device but not another, or why a badge count didn’t update correctly. Good logging was the only way to trace issues through the entire system and keep everything in sync.


From the outside, notifications look simple: a little red badge, a banner, a ping. But behind the scenes, making them reliable, real-time, and cross-platform taught me a lot.