How an architectural decision made 40x growth possible
The problem
When I joined NU Online in 2020, the app had about 40,000 users. It was functional — but it was built as a monolith. Every feature lived in the same codebase, tightly coupled. Adding a new feature risked breaking an existing one. Performance degraded as content grew. Offline access was unreliable.
More fundamentally: the architecture wasn't built for growth. If the app was going to serve hundreds of thousands — or millions — of users, something had to change before we tried to get there, not after.
The team decided to do a full redevelopment. I led the mobile side.
The decisions
Modular architecture — the most important call
The first and most consequential decision was to build v2 as a collection of independent feature modules rather than a single codebase. Each module — Quran, prayer times, zakat, donation, news, waris — would be developed, tested, and deployed independently.
The trade-off was real: this approach is slower to set up. You invest time in the module structure upfront before writing a single feature. In sprint 1, we were slower than the old approach.
The payoff: after launch, the team could work on multiple features in parallel without stepping on each other. Bugs in one module didn't cascade. New features didn't require touching unrelated code. This is what made sustained growth possible — the architecture could absorb continuous improvement without accumulating debt.
Offline-first
NU Online's users are not all in Jakarta with fast internet. Many are in rural areas, pesantren, or low-connectivity environments. Building offline-first wasn't a nice-to-have — it was a user requirement.
The implementation: all content that users are likely to need (Quran pages, prayer times, recent articles) is cached locally on first use. Core features work without internet. Sync happens in the background when connectivity is available.
This also had a performance benefit: reading from local SQLite is faster than waiting for a network response. The app felt faster to everyone, not just users with poor connectivity.
Page Mode Quran — rethinking the Quran feature
Most Quran apps use image-based rendering: each page of the Quran is a PNG file. It looks authentic but costs ~100MB+ of storage for the full 30 juz.
I built a text-based rendering engine instead. The Quran content — structured text with precise line break data — is stored at 11MB. The rendering engine reconstructs the page layout from text using custom Flutter typography, matching the authentic mushaf layout without rasterized images.
The result: ~10x storage reduction, faster loading, and a Quran reading experience that feels native rather than like viewing a PDF.
Prayer time engine — going beyond the library
The Adhan library covers the major global calculation methods. But NU Online's users include Indonesian Muslims who follow Falakiyah NU — a calculation method specific to Nahdlatul Ulama's astronomical standards that doesn't exist in any public library.
I extended Adhan to add: Falakiyah NU (implemented from the official mathematical specification), high-altitude correction, additional global methods (JAKIM, MUIS), madzhab selection for Asr calculation, and full user configurability.
To my knowledge, NU Online remains the only Flutter app with this level of prayer time configurability.
SSO — one login for everything
NU Online runs multiple services: the mobile app, and five web services. I researched SSO solutions and selected Casdoor — an open-source identity platform supporting OAuth2/OIDC. I designed the integration architecture and implemented it across mobile and web.
The result: users have one account across the entire NU Online ecosystem.
The trade-offs
Modular architecture has upfront cost. The first month was slower than if we'd built a monolith. The structure pays for itself over time, but you have to believe in that payoff before you see it.
Offline-first increases complexity. Every feature now has two modes: online and offline. Sync logic, conflict resolution, cache invalidation — all of this is additional surface area for bugs. The trade-off was worth it for NU Online's user base, but it's not free.
The Quran rendering engine took longer than using images. If we'd used the standard image-based approach, the Quran feature would have shipped faster. The text-based engine required custom typography work, line break data processing, and extensive testing to match the mushaf layout. The 10x storage reduction and faster performance justified the investment — but it was an investment.
The outcome
- 40K → 1.6M+ users after v2 relaunch (Oct 2021)
- IDR 13B+ in donations processed (zakat + general)
- Quran storage dropped from ~100MB to 11MB
- Prayer time support for 8 calculation methods, including the custom Falakiyah NU implementation
- One account across the mobile app and 5 web services via SSO
The architecture held. When user growth accelerated, the app didn't break. When new features were added, old features didn't regress. The system was built to absorb growth — and it did.
What I'd do differently
Start the SSO integration earlier. We implemented it midway through the project, which meant retrofitting it into features that had already been built with separate auth. If SSO had been part of the initial architecture, the integration would have been cleaner and faster.
The chat module had encryption hooks. Sodium was already integrated. Adding E2EE should have taken a week. Then Signal Protocol showed up.
The requirement sounded straightforward: build a speech-to-speech AI interface with a smooth Lottie animation. Three things happening simultaneously created a problem we didn't anticipate — the UI and the audio pipeline were competing for the same thread.
The server was hanging with 5 users. Messages were unreliable. The app had no offline capability. These weren't bugs — they were the predictable consequences of an architecture that wasn't designed for real-time communication.