Back to blog

Building Push at Scale: Engineering Billions of Mobile Messages at Emarsys

1 May 20238 min read

Three years on the Push team at Emarsys — how we built event-driven systems delivering billions of personalised push notifications monthly across Angular frontends and RabbitMQ/Pub/Sub backends.

From January 2020 to May 2023 I was a Software Engineer at Emarsys — a B2C marketing platform acquired by SAP — working primarily on the Push team. Our job was to make sure that push notifications reached the right users at the right moment, at a scale that most engineers never have to deal with: billions of personalised messages every month.

Mobile Customer Engagement


What the Push channel does

The Push product lets enterprise brands — think Adidas Runtastic, Petco, Babbel — deliver highly targeted push notifications to their mobile app users. Not batch blasts, but 1:1 messages shaped by purchase history, browsing behaviour, location, cart state, and product affinity.

The key capabilities we owned:

  • Targeted push notifications — personalised by customer segment, product affinity, and behaviour
  • Real-time automated triggers — account creation, cart abandonment, order updates, win-back flows
  • Geo-targeted push — messages triggered when a user enters or exits a physical location
  • Silent push — background syncs for data freshness without user-visible interruptions
  • Cross-channel orchestration — push as one node in a broader email/SMS/in-app journey

Drive mobile revenue with higher app engagement


The scale problem

When Emarsys says billions of messages monthly, that's not marketing copy. The platform serves enterprise clients each with tens of millions of opted-in users. A single campaign send from a large client could mean 20–40M push notifications that need to land within a tight delivery window.

That means the engineering problem isn't just "call APNs and FCM". It's:

  • Rate limiting per app credential without dropping messages
  • Priority queuing so time-sensitive transactional messages beat promotional traffic
  • Delivery guarantees — a message that fails silently is worse than a visible error
  • Observability — knowing in near real-time if a send is stuck, throttled, or bouncing

Send automated push notifications based on real-time triggers


Event-driven backend: RabbitMQ and Google Pub/Sub

The dispatch layer was built around event-driven messaging. Campaign sends produce events onto RabbitMQ queues; consumer workers pick them up, enrich the payload with personalisation data, and fan out to APNs (Apple), FCM (Firebase/Google), and Huawei push gateways.

At the volume we operated:

  • RabbitMQ handled internal service-to-service messaging with fine-grained queue-per-priority routing
  • Google Pub/Sub took over for the highest-volume fan-out paths where we needed horizontal scale without RabbitMQ's operational overhead
  • Redis tracked send state and deduplication so a retry loop couldn't double-deliver to a user

The interesting engineering work was tuning consumer concurrency, backpressure, and dead-letter handling so the system could absorb a large campaign spike without cascading delays into the transactional queue.

Promote your brand in the moment with geo-targeted push


Frontend: Angular campaign builder and analytics

My other half of the work was on the campaign builder UI and analytics dashboards — built in Angular and Vue.js depending on the feature area.

The campaign builder let marketers configure push content, personalisation tokens, send-time optimisation settings, and audience segments. Getting the preview experience right was tricky: a push notification looks different on iOS vs Android, across device generations, and with different notch/dynamic island layouts. We built a live preview component that rendered a realistic device frame and updated in real time as the marketer edited the message.

The analytics side tracked open rates, conversion attribution, revenue per campaign, and user-level engagement history. We used Angular's OnPush change detection throughout to keep dashboards performant when ingesting live metrics over WebSockets.

Enhance your cross-channel user journey


Gain a true view of campaign performance

One thing I'm particularly proud of was our work on attribution. A user opens a push notification, browses the app, then converts 6 hours later via email. Crediting that conversion correctly across channels required a shared attribution model and an analytics pipeline that stitched events across push, email, and in-app touchpoints.

The frontend surfaced this as a per-campaign performance view with revenue impact broken out by channel touchpoint — giving marketers actual evidence of what was working rather than open-rate vanity metrics.

Gain a true view of mobile campaign performance


What I took away

TDD at scale is a productivity multiplier, not a tax. Emarsys ran strong TDD and pair programming culture. At first it felt slow. Within a few months I couldn't imagine shipping backend queue consumers without a test harness — the feedback loop on integration bugs was too expensive otherwise.

Event-driven systems are excellent until you need to debug them. Distributed tracing and structured logging aren't optional. We learned this the hard way when a send appeared to stall and it took hours to trace a missing correlation ID across four services. After that, tracing was a first-class requirement.

Frontend performance in marketing tools matters more than you'd expect. Marketers using the campaign builder all day notice lag. We invested meaningfully in OnPush, virtual scroll for large audience lists, and skeleton loading states — and it showed up in user satisfaction scores.

Three years on that team taught me what high-throughput, event-driven production systems actually look like — and how to build the UIs that make them usable for the humans on the other end.