Push Notification Service Rebuild for a High-Scale Consumer Lending Platform

A consumer lending platform with 2.5M+ users had a push notification service that collapsed under production load. Azati refactored the system from scratch, introducing async queues, timezone-aware batching, composite indexes, and a statistics cache layer, turning a broken tool into a full marketing channel without adding infrastructure the client didn't want.

Scale my push infrastructure
50K+

push notifications processed per day after refactoring

2.5M+

users served on the rebuilt notification infrastructure

5+

months of active development with zero production outages

Technologies used

PHP
PHP
Laravel
Laravel
MySQL
MySQL
PostgreSQL
PostgreSQL
Redis
Redis
Firebase
Firebase
Docker
Docker
Nginx
Nginx

Motivation

Consumer lending platforms live and die by communication timing. A push notification sent at the right moment about the right loan event is a retention tool. One that doesn't arrive is a missed opportunity at best, a broken promise at worst.

The client is a consumer lending platform with 2.5M+ registered users. Their push notification service had been built in-house and it showed: no async processing, no batch splitting, no database indexes. The service was collapsing under production load, notifications weren't being delivered, and marketing campaigns were launching into silence. Azati was brought in to refactor the core, add the missing infrastructure, and build analytics and marketing features on top.

Business challenges

Challenge 01

A synchronous notification engine that collapsed under real load

The original service processed every notification synchronously, with no queue, no batching, and no indexes to support the query patterns needed at scale. At 1M+ users it was already breaking. At 2.5M it would have been completely unusable:

  • No async processing, every send blocked the main thread until completion
  • No batch splitting, all notifications fired in a single synchronous loop
  • No composite indexes on push_logs table
  • Synchronous FCM calls blocking the main thread
  • No dead token cleanup causing delivery failures
#1
Challenge 02

Statistics and analytics without adding new database infrastructure

The marketing team needed a proper analytics dashboard, but the client explicitly didn't want to introduce a columnar database like ClickHouse. The data volume made real-time queries on standard tables impractical:

  • High-cardinality queries across millions of push log records
  • No pre-computation or caching layer, every dashboard request hit the raw tables directly
  • Dashboard queries timing out under load, unusable during peak send periods
  • No deduplication guard for concurrent heavy jobs
#2
Challenge 03

Zero-downtime rewrite with parallel feature development

The service couldn't be taken offline for a migration. Business logic had to be preserved exactly, and new features, tracking, statistics, promo codes, were being scoped and built at the same time as the core refactoring:

  • Existing business logic preserved verbatim
  • New features developed in parallel with refactoring
  • No production downtime during transition
  • Continuous delivery alongside active user base
#3
Challenge 04

Timezone-aware delivery across a geographically distributed user base

Sending 50K notifications in a single batch regardless of recipient timezone meant waking users up in the middle of the night and degrading open rates. Proper delivery required per-user timezone awareness baked into the send pipeline:

  • Per-user timezone resolved before every batch dispatch
  • Suppression window 22:00 to 07:00 local time
  • Four send period types with different batching logic
  • Scheduled sends via Laravel Scheduler and Cron
#4

The client's requirements

The client needed a backend engineer who could inherit a broken codebase, stabilize it, and extend it without disrupting what was already running. The scope grew as the core was stabilized, covering both infrastructure refactoring and new feature development:

  • Fully refactor the push notification engine with async queue processing and batching
  • Design and implement 5+ new database tables and 6+ composite indexes
  • Build FCM token validation pipeline to clean dead tokens automatically
  • Implement timezone-aware batching across four send periods
  • Add conversion tracking for 8 target action types linked to push logs
  • Build statistics system with async pre-computation on PostgreSQL without columnar DB
  • Integrate promo code mechanics with 1C via SOAP for discount notifications
  • Implement automated daily Telegram reporting on push statistics
  • Deliver REST API with API Key authorization and RBAC

Why Azati?

PHP and Laravel depth on a mature, constrained codebase

Inheriting a broken production service and refactoring it without downtime is not a job for someone still learning the framework. The Azati engineer brought fluency in Laravel's queue system, job processing patterns, and database optimization at scale, which meant being productive on a codebase with real constraints from day one.

Engineering creativity within infrastructure constraints

The client's refusal to add ClickHouse or another columnar database was a real constraint. Rather than pushing back, Azati designed an async pre-computation pattern on PostgreSQL with a statistics cache layer and job deduplication guard that delivered the analytics the marketing team needed without touching the infrastructure budget.

Parallel delivery under production pressure

Refactoring the core and building new features simultaneously, on a live platform serving millions of users, requires discipline. The Azati engineer kept both tracks moving, with the core stabilized first and features built cleanly on top, delivering a system that was more reliable and more capable than what it replaced.

Domain knowledge in consumer lending mechanics

Push notifications in a lending context aren't generic marketing blasts. They map to specific loan lifecycle events: application, signing, payment due, overdue, extension offers. Understanding that lifecycle meant the tracking and targeting features were designed to match how the business actually works, not just what was technically convenient.

Dealing with a notification service that can't keep up?

Whether you need to refactor a failing push system or build one from scratch for a high-scale audience, Azati has the backend depth to deliver it reliably.

Let's talk about your backend

Solution

01

Async Notification Engine with Queue and Batching

The core of the refactor was replacing the synchronous send pipeline with a proper async architecture. Laravel Queue with Redis handles all notification jobs, splitting large sends into manageable batches across four time periods: hourly, 3-hour, daily, and weekly. Seven Laravel Commands and six Jobs were written to cover the full send lifecycle, from scheduling through delivery to status tracking.

Key capabilities:
  • Laravel Queue + Redis for async job processing
  • Batch splitting across four configurable send periods
  • 7 Laravel Commands + 6 Jobs covering full send lifecycle
  • Laravel Scheduler + Cron for timed send execution
02

FCM Token Validation Pipeline

Dead FCM tokens are a silent drain on delivery rates. Tokens accumulate as users uninstall apps or switch devices, and sending to invalid tokens counts against quotas and skews delivery metrics. The validation pipeline processes tokens in chunks asynchronously, identifies invalid tokens via FCM response codes, and removes them without blocking the main notification flow.

Key capabilities:
  • Chunked async validation of all stored FCM tokens
  • Automatic removal of invalid and expired tokens
  • Improved FCM delivery rate through clean token registry
  • Non-blocking execution via dedicated background jobs
03

Timezone-Aware Batching

Notifications sent at 3am local time don't get opened. The batching engine resolves each user's timezone at send time and suppresses delivery between 22:00 and 07:00 local time. Users in different regions receive notifications within their active hours, which improves both open rates and the user experience for a platform that operates across multiple time zones.

Key capabilities:
  • Per-user timezone resolution at send time
  • Suppression window 22:00 to 07:00 local time enforced
  • Compatible with all four send period types
  • No additional infrastructure required
04

Statistics Cache and Analytics Dashboard

Real-time analytics queries across millions of push log records on standard relational tables would destroy performance. The solution uses background jobs to pre-compute and cache statistics into a dedicated PostgreSQL statistics_cache table, with a job_dispatched flag preventing duplicate heavy jobs from running simultaneously. Three table types cover different reporting dimensions. The dashboard surfaces these pre-computed results instantly, with Excel export for overdue user lists.

Key capabilities:
  • Async pre-computation into statistics_cache (PostgreSQL)
  • job_dispatched deduplication guard for concurrent jobs
  • Three statistics table types covering key reporting dimensions
  • Dashboard with Excel export via maatwebsite/excel
05

Conversion Tracking and Promo Code Mechanics

The marketing team needed attribution beyond opens. Eight key loan lifecycle actions were tracked back to the originating push notification, including applications, signings, payments, repayments, extensions, offers, and reviews. Click tracking was implemented using short URLs, and a promo code system was added with a daily limit of 20 codes and SOAP integration with the client's 1C system for automatic discount application.

Key capabilities:
  • 8 target action types tracked via PushLog linkage
  • Short URL click tracking per notification
  • Promo code generation with daily limits
  • 1C SOAP integration for discount application to orders
06

Automated Telegram Reporting

The operations and marketing teams receive automated daily reports via Telegram covering key push statistics: send volumes, delivery rates, conversion by action type, and token health. Reports are generated by scheduled jobs and dispatched via Telegram API, keeping the team informed without requiring anyone to log into a dashboard.

Key capabilities:
  • Daily automated Telegram reports via Telegram API
  • Push volume, delivery rate, and conversion metrics
  • Scheduled via Laravel Scheduler
  • No manual reporting work required

Major achievements

Metric / areaBefore engagement with AzatiAfter engagement with Azati
Service stabilityCollapsed under production load0 outages after go-live on refactored system
Daily notification volumeCould not reliably deliver at scale50K+ notifications processed per day
User base served~1M at time of failure2.5M+ users on rebuilt infrastructure
FCM delivery rateDegraded by dead token accumulationImproved via automated token cleanup pipeline
Conversion trackingNo funnel visibility8 action types tracked from notification to completion
Statistics dashboardNot functional at scaleAsync pre-computed cache, instant dashboard queries
Marketing automationManual, unreliablePromo codes, timezone batching, daily Telegram reports

Security

All external API endpoints are protected by API Key middleware with Laravel Sanctum handling authentication. Role-based access control is enforced via spatie/laravel-permission (RBAC) throughout the admin layer. Rollbar error monitoring and Laravel Telescope provide full audit trails of requests and job execution. All traffic runs over SSL/TLS with custom certificates. The system operates in compliance with applicable data protection requirements for financial platforms handling personal user data.

Engagement & delivery

Single backend engineer, full delivery ownership

The engagement ran with one Azati backend engineer responsible for the full scope: refactoring the core, designing the database schema changes, building new features, and integrating with external systems. Working inside the client's Agile team meant participating in sprint planning and delivering continuously against a live production system.

Refactoring and feature development in parallel

The core engine and new features weren't developed sequentially. The refactoring was prioritized to stabilize the service first, with new capabilities built on top as the foundation solidified:

  • Core async engine stabilized before feature work began
  • New tables and indexes designed alongside refactoring
  • Tracking, statistics, and promo code features built on stable core
  • No production downtime throughout the engagement

Results & business impact

A Broken Tool Became a Marketing Channel

Before the engagement, the notification service was a liability. After it, the marketing team had a stable, analytics-equipped channel with conversion tracking, promo code mechanics, and automated reporting. A tool that was silent became one of the platform's active retention instruments.

Zero Outages at 2.5M+ User Scale

The refactored service has not gone down under production load. It now processes 50K+ notifications per day across a user base that has grown to 2.5M+ since the refactoring, with no service failures.

Full Funnel Visibility for Marketing

For the first time, the marketing team could see which notifications drove loan applications, payments, and extensions. Eight tracked action types gave them a complete picture of notification-to-conversion performance, enabling data-driven campaign decisions.

Analytics Without Infrastructure Overhead

The async pre-computation pattern on PostgreSQL delivered the statistics layer the team needed without adding ClickHouse or any other specialist database. The solution fits within existing infrastructure and performs well at the current data volumes.

FinTech Notification Engineering Expertise Retained

The engagement built expertise inside Azati in high-load notification architecture, Laravel queue patterns at scale, FCM delivery optimization, and the consumer lending lifecycle that shapes how push campaigns are structured in this domain.

Strategic wins

A few patterns from this engagement that go beyond the specific deliverables:

Async pre-computation as an alternative to columnar databases

The statistics_cache pattern, where background jobs pre-compute heavy aggregations and store results with a deduplication guard, is a reusable solution for teams that need analytics performance but can't or won't add ClickHouse, BigQuery, or similar tooling. It works within standard relational infrastructure and scales reasonably well for mid-size data volumes.

Timezone-aware batching as a delivery quality standard

Sending at the right local time isn't just user experience polish, it directly affects open rates and conversion. The pattern of resolving user timezone at send time and applying a suppression window is reusable for any platform with a geographically distributed user base and time-sensitive notifications.

Refactoring without regression requires discipline, not just skill

The hardest part of this project wasn't writing new code. It was preserving existing business logic exactly while restructuring the system around it. That requires careful reading of the original codebase, disciplined testing, and a willingness to move slowly on the parts that matter most before accelerating on new features.

Dead token cleanup as a delivery rate lever

FCM delivery rates degrade silently as invalid tokens accumulate. The chunked async validation pipeline that identifies and removes dead tokens is the kind of maintenance work that gets skipped until it becomes a visible problem. Building it into the core infrastructure means the client's delivery metrics stay accurate as the user base grows.

Team composition

The engagement ran with a single Azati backend engineer covering the full scope of refactoring and feature development over five months of active work on the service.

  • Backend Engineer (PHP/Laravel) sole Azati contributor. Responsible for full refactoring of the notification engine, database schema design (5+ new tables, 6+ composite indexes), async job architecture (7 Commands, 6 Jobs), FCM integration and token validation, statistics cache system, conversion tracking, promo code mechanics, 1C SOAP integration, Telegram reporting, REST API development, and security layer implementation.

The described expertise is relevant for

  • High-load notification and messaging systems requiring refactoring
  • Laravel and PHP backend platforms at 1M+ user scale
  • Firebase FCM integration and delivery rate optimization
  • Analytics engineering without specialist database infrastructure
  • FinTech and consumer lending platform backend development
  • Zero-downtime system rewrites with preserved business logic

Last updated

Got a job for Azati? Let’s talk business!

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

What's next?

  • 1. Tell Us Your Story
    Describe your project. We come back within 24 hours with team availability and a rough plan. NDA on request before the first call.
  • 2. Get Your Roadmap
    Receive a detailed proposal with scope, team composition, timeline, and costs tailored to your goals.
  • 3. Start Building
    Azati aligns on details, finalize terms, and launch your project with full transparency.