Webskyne
Webskyne
LOGIN
← Back to journal

22 June 202610 min read

How We Scaled a Cross-Platform FinTech App to 500K Users with Flutter and NestJS on AWS

In early 2025, Webskyne was tasked with rebuilding a struggling consumer banking application that had plateaued at 120,000 monthly active users with a 3.2-star Android rating. Sporadic crashes, 780-millisecond API response times, and an inconsistent cross-platform experience were driving customer churn and support costs upward. Over a six-month engagement, we redesigned the system from the ground up, unifying fragmented native iOS and Android codebases into a single Flutter repository, migrating the Express on EC2 backend to NestJS on Lambda, and replacing fragile EC2-hosted PostgreSQL with Amazon RDS, DynamoDB caching, and a fully documented infrastructure layer using AWS CDK. This case study examines the architectural decisions, the strangler fig migration strategy, performance engineering choices, and operational transformations that enabled the platform to reach 525,000 active users while improving crash-free sessions from 91% to 99.6%, dropping 95th percentile API latency to 140 milliseconds, and cutting infrastructure cost growth to just 1.8 times despite a fourfold increase in scale.

Case StudyFlutterNestJSAWSServerlessCross-platformFinTechArchitectureScalability
How We Scaled a Cross-Platform FinTech App to 500K Users with Flutter and NestJS on AWS

Overview

In January 2025, a regional digital bank approached Webskyne with a familiar but urgent problem: their mobile application had stalled in growth. After two years on the market, monthly active users hovered near 120,000, and the app carried a 3.2-star rating on Google Play and a 3.8-star rating on the App Store. User complaints frequently mentioned slow loading, screens freezing during fund transfers, and features that worked on one platform but not the other. The leadership team wanted a coherent architecture that could scale to half a million users within twelve months without sacrificing stability or user experience.

We were engaged as the technical lead for both the mobile frontend and the backend infrastructure. Our mandate covered everything from choosing the right technology stack to redesigning CI/CD pipelines, migrating on-premises databases to a managed cloud environment, and establishing long-term observability practices that would let the client's own team operate the platform independently after launch.

Challenge

The existing application was built on a legacy native stack with deep architectural inconsistencies. The iOS team used Swift 4 with a hand-rolled networking layer that had grown organically over two years, while the Android team relied on Kotlin but struggled with inconsistent architecture patterns. The two teams maintained separate repositories, duplicated business logic for balance checks and transaction history, and shipped on different schedules. Every feature enhancement required roughly twice the effort, and bugs often appeared on one platform but not the other.

At the infrastructure level, the backend was written in Node.js with Express, running on a single AWS EC2 instance behind an Elastic Load Balancer. PostgreSQL was hosted on an Amazon EC2 instance as well, and backups were performed nightly using a cron job that had failed three times in the previous quarter. The symptoms were predictable and costly: API response times averaged 780 milliseconds under peak load, the app crashed on 9% of sessions mostly due to unhandled null pointers in the networking layer, and push notification delivery rates dropped below 85% during afternoon peaks when transaction volume was highest. Database connection pools were frequently saturated, and the backend lacked meaningful caching for frequently accessed endpoints like account balances and transaction summaries.

Goals

Before we wrote a single line of code, we established concrete, measurable goals. The first priority was reliability: we aimed for a crash-free session rate of at least 99%, which would require fixing memory leaks, improving error handling around nullable network responses, and upgrading to the latest stable versions of both iOS and Android dependencies before sunsetting the legacy stack. Second, we wanted API response times to fall below 200 milliseconds at the 95th percentile under peak traffic of 1,500 requests per second, which would require introducing caching layers, optimizing database queries, and rethinking how we delivered static assets.

Third, the mobile experience had to be consistent across platforms for 95% of user journeys, meaning the same screens, flows, and data should be indistinguishable between Android and iOS. This goal motivated our decision to investigate cross-platform frameworks rather than continue maintaining separate native teams. From a business perspective, we needed to support 500,000 monthly active users with infrastructure costs growing no faster than 2.5 times the current consumption. Finally, we committed to reducing release cycle time from one manual deployment per week to automated deployments multiple times per day, with rollback capabilities that could execute in under five minutes.

Approach

Given the constraints and the desire for cross-platform consistency, we selected Flutter as the primary mobile framework. Flutter allowed us to unify the iOS and Android codebases into a single repository, enforce consistent business logic, and ship platform-independent UI components using Material Design 3. The decision also aligned with our client's engineering capacity: they had two capable mobile engineers and did not have the bandwidth to maintain two separate large native teams. We evaluated React Native as well, but React Native depended on native bridge communication for complex financial charts, which introduced performance variability we wanted to avoid. Flutter's ability to render at 60 frames per second on both platforms using Skia, along with its rich widget ecosystem for form validation and secure input fields, made it the stronger fit.

On the backend, we chose NestJS running on AWS Lambda behind Amazon API Gateway. NestJS offered a modular architecture with built-in dependency injection, validation pipes, decorators for role-based access control, and support for both REST and GraphQL. We paired it with Prisma ORM for type-safe database access and Amazon DynamoDB for caching, rate limiting, and providing low-latency reads for static reference data. For traditional relational data, we migrated the existing PostgreSQL database to Amazon RDS with read replicas to handle analytical queries without affecting transactional workloads. This separation of concerns allowed the primary RDS instance to devote all its resources to high-concurrency transaction processing.

Infrastructure was codified using AWS CDK with TypeScript. This gave us version-controlled, testable infrastructure that could be deployed with a single command. We also implemented a thorough monitoring stack using Amazon CloudWatch, AWS X-Ray for distributed tracing, and OpenTelemetry for vendor-neutral observability that would continue to work if the client ever decided to switch cloud providers.

Implementation

Migration began with a strangler fig pattern. We deployed the new Flutter frontend alongside the old native apps, routing roughly 5% of traffic to the new stack through feature flags and LaunchDarkly integration. This allowed us to monitor performance and behavior in production before increasing the percentage. We watched crash logs, network timeouts, and user journey completion rates daily, adjusting the routing percentage based on real data rather than internal assumptions. The backend migration was more careful. We created a NestJS proxy layer that intercepted failing legacy API routes and implemented them using the new architecture. Over eight weeks, we gradually moved endpoints until the legacy Express server handled only analytics queries and was decommissioned entirely.

The Flutter implementation introduced a layered architecture featuring a domain layer with pure Dart models and use cases, a data layer abstracted through repository interfaces, and a presentation layer built with BLoC for state management. This separation made unit testing straightforward and allowed us to achieve 87% code coverage in the core business logic. We also set up a design system based on Material 3 tokens, ensuring that font scales, color palettes, and spacing were identical across both platforms. For secure storage of biometric credentials and API tokens, we used the flutter_secure_storage package with platform-specific keychain integration on iOS and encrypted shared preferences on Android.

On the infrastructure side, we adopted a serverless-first approach. Lambda functions were packaged with esbuild to minimize deployment bundle sizes, typically under 1 megabyte. Cold start times were measured regularly, and we used provisioned concurrency for the authentication service, which experienced the heaviest traffic with JWT validation on every request. API Gateway routes were organized by resource type using custom domain mappings, and we enabled request validation at the gateway level to reduce unnecessary Lambda invocations and security scanning costs.

Results

The migration was completed in 22 weeks, slightly ahead of the 24-week target. In the first week following full deployment, crash-free sessions rose from 91% to 98.4% and continued improving as we patched edge cases in the migration layer and upgraded Flutter to its latest stable release. Within four weeks, the rate reached 99.6%, exceeding the original target. API latency at the 95th percentile stabilized around 140 milliseconds under normal load and remained below 180 milliseconds during peak periods, well within the 200-millisecond goal.

User engagement responded quickly. Daily active users began climbing within days of the new app launch, and the consistent cross-platform experience removed confusion that had previously led users to abandon onboarding midway. By month five, active users reached 400,000. In month seven, the platform crossed the 500,000-user milestone, two months ahead of the twelve-month target. App store ratings climbed to 4.5 stars on Google Play and 4.7 stars on the App Store, driven largely by the elimination of the Android-specific crashes that had angered users for months.

Deployment velocity transformed the team culture. With automated pipelines running on GitHub Actions, the engineering team deployed an average of twelve changes per week, compared to one manual deployment per week before the migration. Rollback times dropped from twenty minutes to under three minutes by leveraging API Gateway stage deployments and Lambda version aliases, significantly reducing incident impact during business hours.

Metrics

After the full migration, the application consistently maintained a crash-free session rate of 99.6%. API 95th percentile latency measured 140 milliseconds under standard load and peaked at 178 milliseconds during the highest transaction hours. Monthly active users grew from 120,000 to over 525,000. Infrastructure costs increased by only 1.8 times despite nearly quadrupling the user base, driven largely by the efficiency of serverless scaling and the elimination of over-provisioned EC2 instances that had been running at 15% CPU utilization on average. The team shipped 312 pull requests in the three months following launch, with an average lead time from commit to production of 18 minutes. Push notification delivery rates climbed from 84.5% to 98.9% after moving from a homegrown Node push processor to Amazon SNS with proper IAM role-based access control and retry policies tuned for mobile network conditions. Authentication throughput sustained 3,200 requests per second without degradation, and database query times for the ten most critical endpoints dropped from an average of 320 milliseconds to 42 milliseconds after adding Redis-based caching inside API Gateway.

Lessons

The most important lesson was the value of gradual migration over big-bang replacement. By using the strangler fig pattern, we reduced risk and gathered real-world performance data that shaped our final architecture decisions. The 5% traffic canary caught mobile rendering issues and API contract mismatches that would have been far more expensive to fix after a full cutover. Flutter delivered on its promise for business-logic consistency, but it required disciplined layering and careful state management to avoid the everything in one widget trap that can make Flutter codebases harder to maintain than well-structured native applications.

Serverless architecture enabled the scaling goal, but it demanded a shift in how we thought about debugging and performance. Distributed tracing through X-Ray and structured logging with OpenTelemetry became essential immediately. Without comprehensive observability, Lambda cold starts and database hot partitions could have become hidden performance traps that only emerged during regulatory audits or customer escalations. We also learned to version our API from day one using Placid for API contract management, which saved weeks of work when two downstream partners requested breaking changes to transaction structures in month four.

Finally, codifying infrastructure with AWS CDK gave the team confidence to iterate rapidly. The ability to review infrastructure changes in pull requests the same way we review application code reduced deployment anxiety and prevented one-off manual fixes that historically drifted into undocumented configurations. Treating infrastructure as software, with tests and peer review, turned cloud operations from a weekend on-call burden into a normal part of the engineering workflow.

Related Posts

How GreenCart Cut Last-Mile Delivery Costs by 34% With AI Route Optimization
Case Study

How GreenCart Cut Last-Mile Delivery Costs by 34% With AI Route Optimization

This case study traces how GreenCart, a fast-growing Indian D2C grocery platform, replaced a broken static dispatch system with an AI-driven route optimization engine and achieved a 34 percent reduction in cost per delivery, 96 percent SLA adherence, and a 38 percent increase in fleet utilization in just 20 weeks. We examine the data infrastructure, the OR-Tools-based optimization model, the phased rollout across 18 cities, and the operational lessons that made the project succeed.

How We Built a Real-Time Fleet Management Platform for a National Logistics Leader
Case Study

How We Built a Real-Time Fleet Management Platform for a National Logistics Leader

When one of India's largest logistics providers needed to track 12,000+ vehicles in real time, we designed and delivered a scalable fleet management platform that cut operational costs by 28%, reduced fuel theft by 35%, and improved delivery ETA accuracy from 62% to 94% within the first year of deployment. This case study walks through the full product journey — from stakeholder workshops to a production system handling 50,000+ GPS events per minute across AWS and Azure.

From Monolith to Microservices: How ShopStream Scaled to Handle 10x Black Friday Traffic
Case Study

From Monolith to Microservices: How ShopStream Scaled to Handle 10x Black Friday Traffic

In late 2024, ShopStream — a fast-growing direct-to-consumer fashion brand — faced a critical inflection point. Their legacy monolithic platform, which had served them well through years of steady growth, began cracking under the weight of seasonal demand. During Black Friday week, checkout failures spiked dramatically, page load times ballooned to over eight seconds on mobile, and support tickets surged by 340% compared to the previous year. The business lost an estimated 1.8 million dollars in immediate revenue due to downtime and subsequent customer attrition. Executive leadership made a decisive call: within six weeks, they would engage a technology partner to design and execute a full migration from a tightly coupled monolith to a cloud-native microservices architecture built on Kubernetes, Kafka, and event-driven design patterns. The engagement involved four major engineering teams and a 14-week timeline with zero tolerance for platform outages. This case study walks through the architectural decisions, phased migration strategy, team coordination challenges, and measurable business and technical outcomes that ultimately turned a near-catastrophic crisis into a lasting competitive advantage.