Quran Memorization · Engineering Case Study

Quran Memorization — Engineering Case Study

How a local-first Android app was engineered to preserve memorization progress, handle backup and restore safely, and remain reliable offline.

Quran Memorization is a personal Android product I own end to end — product design, architecture, implementation, testing, and release work. The app helps users memorize the Quran over time, which makes it significantly more complex than a reader: progress is not just which page was opened, but a persistent history of what was memorized, how recall was tested, and what should be reinforced next. The case study below explains how that problem shaped the architecture.

Problem and context

A reader can be stateless beyond bookmarks. A memorization companion cannot. Users create a personal learning history that grows over weeks and months, and losing it would mean losing real effort, not just cached data.

The constraints were practical: the app needed to remain useful offline, start quickly, survive process restarts, and keep relationships between learning entities coherent. At the same time it should allow recovery and portability across devices without introducing a permanently available backend that the core experience depends on.

That set the direction: treat local persistence as the source of truth, make backup a recovery mechanism rather than a live database, and design every write, migration, and restore around not corrupting relationships.

Domain model

The domain was modelled to reflect how memorization actually happens, rather than mirroring the Quran text structure alone.

A Journey represents a user’s intention to memorize a contiguous range. A Chunk breaks that Journey into a manageable piece the user can work on in a single session. Chunks organize Ayahs, and within an Ayah the content can be addressed in Parts so the user can focus on smaller segments before assembling the whole. Journey and Chunk define the organizational structure of a memorization plan, while durable learning state belongs primarily to the Ayah and its Parts.

Ayah-level memorization state, Part progress, review scheduling via ReviewSchedule, and historical sessions recording recall and reinforcement outcomes with timestamps persist independently of transient UI state and must survive changes to the surrounding organizational structure. Ordering and references still matter structurally — a misordered Chunk or a missing Ayah link would silently change what the user had planned — but the durable history itself is anchored at the Ayah and Part level. Learning history remains independent of mutable Journey/Chunk organization where appropriate.

The implementation consequence was to model these concepts as relational entities with explicit ordering, foreign keys, and timestamps, and to make UI and navigation reflect persisted state rather than in-memory progress. This keeps recall and reinforcement workflows deterministic after a restart and makes the history itself a durable asset.

Local-first architecture

Core user data is locally owned. The app is built with Kotlin, Jetpack Compose, and Room, where Room is the durable source of local persisted state and the UI observes it directly.

Problem: if core flows depend on a remote service, the app becomes unavailable offline and every read path must handle network failure.

Constraint: memorization is practiced daily and often without reliable connectivity. The user should be able to start a session, complete recall, and see accurate progress regardless of network.

Decision: local-first, single source of truth in Room, with Google Drive used only for backup and portability via the appDataFolder and the drive.appdata scope.

Implementation: navigational state such as the current Journey, active Chunk, and review queue is derived from persisted records. Writes go through Room transactions before they affect what the user sees, so a crash cannot leave the UI showing progress that was never saved. The app does not assume a backend is reachable for ordinary reading, practice, or review.

Verification: Room migrations and ordering were verified through unit and instrumentation tests. Offline, process-death, and reboot behavior was exercised through automated tests and API 35 emulator validation, complemented by targeted checks on a physical Xiaomi device for important real-device workflows.

Result: the app remains useful without connectivity, recovery is a distinct concern from daily use, and the question of where truth lives has a single answer.

Important engineering decisions

Several decisions were made to protect that local-first invariant:

Room as the system of record. Rather than caching remote data, local tables own identifiers, ordering, and history. This simplifies reasoning about what would happen if a backup were delayed, corrupted, or unavailable: daily memorization continues unchanged.

Explicit relationship integrity over flexible documents. Storing Journeys, Chunks, Ayahs, and Parts as related rows with foreign keys enforces that ordering and references stay coherent across a snapshot. A backup that restores unrelated fragments would be rejected rather than merged silently.

Portable JSON representation at the boundary. Backup content is serialized as portable JSON rather than a database dump. That creates a schema and version boundary where UTF-8, structure, and size can be validated before any local data is overwritten.

Destructive actions require explicit confirmation. Restore replaces the local dataset. That operation is framed in the UI as destructive and requires an additional confirmation step after preview, so the user understands that newer memorization history could be overwritten.

Backup and restore reliability

The central risk for a local-first product with optional cloud backup is captured in one sentence:

A backup feature becomes dangerous when restoring it can silently corrupt relationships or overwrite newer memorization history.

The implementation addresses that risk in layers.

Problem: users need recovery if a device is lost or reset, but naive copy-and-replace can corrupt foreign keys, accept damaged data, or overwrite progress that was created after a backup was made.

Constraint: Google Drive is the portability layer, not the runtime database. The app uses the appDataFolder with the drive.appdata scope so backup data remains scoped to the app. Network availability, quota, and concurrent modification cannot be assumed.

Decision: treat backup and restore as a careful transactional boundary with validation, conflict detection, and explicit user decisions.

Implementation:

  • Backups are produced as a coherent snapshot of Room state, serialized to portable JSON with an explicit schema and version. Size is bounded so an unexpectedly large payload is refused rather than persisted blindly.
  • Before any restore, the payload is validated for UTF-8 correctness, structural shape, and referential coherence. The user sees a restore preview summarizing what would change, rather than being asked to trust a filename.
  • Restoration executes transactionally and is written to be foreign-key-safe: related rows are restored in an order that preserves references, and the entire replacement either succeeds or leaves the previous dataset intact.
  • Remote conflict handling uses the Drive ETag. When the app uploads a backup it captures the remote ETag, and subsequent updates use a conditional If-Match request. If Drive responds with HTTP 412 the app surfaces a typed remote-backup conflict instead of overwriting the remote version blindly.
  • After a preview has been accepted for restore, the app also protects against overwriting newer local state: if additional memorization progress was recorded locally after the preview was created, that progress is not silently discarded.
  • Damaged or structurally invalid backups are rejected with an explanatory error rather than producing a partially restored dataset.
  • Backup scheduling is network-constrained and periodic, and disconnect and reconnect behavior was tested to ensure the account can be disconnected and reconnected without leaking state or leaving the app in an ambiguous backup identity.

Verification: Automated and instrumentation tests covered validation failures, transactional restoration, and recovery and error paths including damaged-backup rejection and handling of network interruptions. API 35 emulator validation exercised the broader automated integration suite, while targeted Xiaomi acceptance covered real Google authorization, Drive backup discovery, restore cancellation preserving local data, disconnect and reconnect, meaningful backup creation, conflict handling including Keep This Device, cross-device restore, and automatic-backup state surviving restart.

Result: recovery exists for real device loss, but cannot silently undo newer learning history or leave the local database in an inconsistent state.

Accessibility and Arabic RTL

Accessibility was treated as part of correctness rather than polish.

Problem: the primary content is Arabic Quran text, which introduces right-to-left rendering, and the app’s actions include nearby destructive operations that must remain unambiguous for all users.

Implementation:

  • Interactive controls carry meaningful semantic labels so TalkBack workflows communicate purpose rather than reading generic or duplicated text. Reachable controls and adequate touch targets were verified alongside visual layout.
  • Arabic text is rendered with correct directionality and right-aligned, wrapped layout so verses remain readable across widths. Care was taken that long Ayah strings and wrapped lines do not break reading order or overflow the viewport.
  • Large and XXL text configurations were validated so that memorization, recall, review, and settings screens remain usable when system font scale increases.
  • Destructive and important actions, including the backup restore confirmation, were made to remain understandable both visually and through assistive technology, without relying on color alone.

Verification: TalkBack workflows were manually validated on a physical Android device alongside emulator checks. No formal WCAG certification is claimed; verification focused on what was actually implemented and exercised.

Testing and verification

Confidence in the points above came from layered testing, not a single suite.

Unit tests cover domain logic, state transitions, and validation of the backup representation. Compose and instrumentation tests exercise navigation, persisted state display, and critical user flows against a real Room database. API 35 emulator validation provides coverage of current platform behavior, complemented by targeted manual validation on a physical Xiaomi device for touch, RTL rendering, and performance feel.

Backup and restore received dedicated validation. Automated tests covered conflict, preview, confirmation, damaged-backup, and transactional paths and exercised failure recovery, while API 35 emulator validation covered the broader integration suite and targeted Xiaomi acceptance focused on real Google authorization, backup discovery, conflict handling, disconnect and reconnect, backup creation, and cross-device restore. Release build verification, Android lint, and build gates are part of the hardening process, and performance was observed during release hardening rather than asserted through invented benchmarks.

The aim was not a reported percentage but that the risky paths — migration, transactional restore, conflict, and accessibility navigation — were exercised in an environment close to real use.

Release engineering and privacy

Quran Memorization remains in development and is not presented as publicly released on Google Play. Release engineering was still brought to a state where production risks had been considered.

Crash reporting is intentionally separated: Crashlytics is enabled for release builds and disabled for debug so development diagnostics do not pollute production signal. Privacy-policy integration was included so backup handling and diagnostics are described accurately. Release signing, target-SDK and readiness checks, signed Android App Bundle generation, and production-hardening checks are part of the workflow, with backup handling reviewed for privacy-conscious scope and data minimization.

Engineering takeaways

The work reinforced a few principles that extend beyond the Quran domain:

  • Model the user history as a first-class durable concept. If relationships and timestamps can be lost, the feature is incomplete.
  • Make the source of truth explicit. A local-first choice simplifies offline behavior, but it requires disciplined backup boundaries and transactional restore.
  • Treat destructive recovery as a user-facing safety problem, not just a data problem. Preview and explicit confirmation exist to protect memorization effort.
  • Validate accessibility and RTL by exercising them on real devices with real font and assistive-technology settings.

The case study complements the Product Overview: the overview explains what the product is, while this narrative explains how it was engineered, why the tradeoffs were chosen, and how the result was verified.