The Problem
PhonePe's login screen had a 15% failure rate. That means roughly 1 in 7 users couldn't log in on their first attempt. At 500M+ users, that's tens of millions of frustrated people.
I was tasked with fixing it. Here's the complete journey.
Understanding the Failures
First, I instrumented every possible failure point:
- OTP never received (38% of failures) — Network issues, SMS gateway delays, or incorrect phone numbers
- OTP expired (24% of failures) — Users took too long to enter the code
- Wrong OTP entered (19% of failures) — Typos, confusion with other OTPs
- App crash during login (11% of failures) — Edge cases in the auth flow
- Network timeout (8% of failures) — Slow or unstable connections
The Solutions
Fix 1: Auto-read OTP with SMS Retriever API
The biggest win. Instead of asking users to manually type a 6-digit OTP, we implemented the SMS Retriever API to auto-read and auto-fill.
val client = SmsRetriever.getClient(context)val task = client.startSmsRetriever()
task.addOnSuccessListener {
// Register broadcast receiver for incoming SMS
registerReceiver(smsReceiver, intentFilter)
}
Impact: OTP-related failures dropped by 45%.
Fix 2: Graceful Retry with Exponential Backoff
Instead of showing a generic "Something went wrong" error, we implemented smart retry:
- First failure: Auto-retry silently after 2 seconds
- Second failure: Show "Retrying..." with a spinner
- Third failure: Offer alternatives (call-based OTP, WhatsApp OTP)
Impact: Network timeout failures dropped by 60%.
Fix 3: OTP Timer Extension
Our OTP expired after 30 seconds. User research showed that 35% of users on low-end devices needed more than 30 seconds (app switching to SMS app and back was slow). We extended to 60 seconds and added a visible countdown.
Impact: OTP expiry failures dropped by 50%.
Fix 4: Phone Number Validation
We added real-time validation as the user types:
- Format check (10 digits, valid prefix)
- Carrier detection (show carrier name for confirmation)
- Previously used numbers (auto-suggest from account history)
Impact: Wrong number submissions dropped by 70%.
Fix 5: Crash-Free Login Flow
We found 8 edge cases causing crashes:
- Config change during OTP verification
- Back press during API call
- Dual SIM number picker dismissal
- Low memory during biometric prompt
Each was fixed with proper lifecycle handling and state restoration.
Impact: Auth-related crashes went to zero.
The Design Changes
Beyond technical fixes, we redesigned the UX:
- Single-screen flow — Previously, phone number and OTP were on separate screens. We combined them with a smooth transition. Fewer navigation steps = fewer drop-offs.
- Trust signals — Added PhonePe's security badge, "256-bit encrypted" text, and RBI compliance notice. Users are entering sensitive info — they need reassurance.
- Error messages that help — Changed "Invalid OTP" to "That code didn't match. Check your SMS and try again." Changed "Network error" to "Slow connection. We'll keep trying."
- Loading states — Replaced blank screens during API calls with skeleton loaders and progress indicators. Users who see progress are 3x more patient.
The Results
After 6 weeks of incremental rollout:
- Login failure rate: 15% → 6% (60% reduction)
- Average login time: 28 seconds → 12 seconds
- Support tickets for login issues: -73%
- User satisfaction (login NPS): +22 points
Key Lessons
- Instrument everything — You can't fix what you can't measure. Every failure mode needs a distinct analytics event.
- Don't blame the user — "Wrong OTP" is a UX failure, not a user failure. Make it harder to fail.
- Test on real conditions — Our lab testing missed issues that only appeared on 2G networks with 512MB RAM devices.
- Small changes, big impact — The OTP timer change was a one-line code change that eliminated 12% of all login failures.
Login is your app's front door. Make it effortless.