It reads the entire Quran in Arabic with translations in dozens of languages. It computes prayer times for any coordinate on earth, points a compass at Mecca, schedules your memorization review with a spaced-repetition algorithm, and — if you opt in — syncs all of it across your phone, laptop, and browser extension without the server ever being able to read a byte of it.
And it has no database.
Ummah Library is an open-source Quran platform (AGPL-3.0). This series takes it apart to show how it's built, because the way it's built is worth stealing. This first article is the map: the shape of the codebase, the one rule that holds it together, and where the deep-dives go from here.
Everything lives in one pnpm workspace, orchestrated by Turborepo. The workspace globs are as boring as you'd hope:
apps/ holds the things a user opens; packages/ holds the things those apps are
built from.
Three user-facing apps, one shared brain. The mobile app is not a rewrite of the web
app's logic; the extension is not a third copy. They import the same core and render
the same design system. That's the whole game: write the domain once, present it
three ways.
If you remember one thing about this codebase, remember the direction of its arrows.
“Dependencies point inward.
appsmay usepackages;packagesmay never useapps; and core depends on nothing at all.”
This is the ports-and-adapters pattern (also called hexagonal architecture), and it's
recorded in ADR 0001. Here is the actual dependency
graph, straight from the repo's ARCHITECTURE.md:
Notice that every arrow eventually points at core, and no arrow leaves it. core
is the centre. It defines the entities (Surah, Ayah, VerseKey, Hadith…)
and the ports — interfaces like QuranRepository and PrayerTimesCalculator.
Everything else in the system either implements one of those interfaces or consumes
it. Nothing in core knows whether the Quran text comes from a file, an HTTP call, or
a SQLite table; it only knows the shape of the answer.
The payoff of that constraint is the subject of the next two articles, so I'll keep it to a sentence here: because the domain depends on nothing, it can be tested in isolation, shared across every platform, and outlive whatever framework is fashionable this year. The frameworks live at the edges, behind the ports, where they can be swapped without the middle noticing.
Abstractions are easier to trust when you can trace one concrete path through them. Say a build worker needs the text of Surah 2 (Al-Baqarah, "The Cow"). Here's the trip:
The api package holds the wiring. It picks which concrete implementation stands
behind each port — and it reads like a list of decisions:
Look at the types. Every left-hand side is a core interface; every right-hand side
is a concrete adapter. The Quran text comes from a file (FileQuranRepository),
tafsir (classical commentary) is fetched over HTTP at runtime
(HttpTafsirRepository), prayer times are computed by a third-party astronomy
library wrapped in AdhanPrayerTimes. The rest of the application — every page,
every API route — depends only on the interface on the left. Swap a right-hand side
and nothing above notices.
That same wiring is exposed two ways, so two very different consumers can share one implementation (ADR 0004): a tRPC router for end-to-end type safety inside our own TypeScript apps, and a plain REST API for everyone else.
The mobile app imports that AppRouter type and gets fully-checked API calls for
free. A stranger's Python script hits GET /api/v1/surahs/2 and gets the same data as
static JSON. One set of repositories, two surfaces.
core is where the interesting, transferable engineering is, so it's worth seeing how
much of the product is pure logic with no I/O. The package's barrel file is a table
of contents for the domain:
Every one of those modules is a plain function library: give it inputs, get outputs, no side effects. The memorization scheduler doesn't know what a database is. The prayer-time helpers don't read the system clock — you pass them the current time. The search ranker is a pure function over an array. Roughly thirty modules, and nearly every one ships with a co-located unit-test file, because pure functions are trivial to test. (That "pass in the clock" habit is small but load-bearing; article 3 is entirely about it.)
Two Arabic terms you'll meet throughout, defined once: an ayah is a verse; a surah is a chapter. The Quran has 114 surahs and 6,236 ayahs, and those two numbers are treated as invariants the build checks against — more on that in article 11.
One more thing that makes this codebase unusual, and unusually easy to join: it
explains itself. The docs/adr/ directory holds three dozen Architecture
Decision Records — short documents, each in the same shape (Context → Decision →
Consequences), that capture why a choice was made and what it cost.
They are not marketing. They argue with each other. ADR 0012 sets up a pattern for prayer times; ADR 0013 turns around and says that pattern was the wrong shape for the qibla and explains why. That thread is so instructive it gets its own article. The point for now: when you want to understand any part of this system, there is almost always a numbered record telling you what the author was thinking. That's a rare gift in open source, and it's why this series can cite a reason for nearly every claim it makes.
The rest of the series follows the arrows inward and then back out:
Date.now() is a build error inside
core, and what determinism buys you.Here's the thing about a codebase with arrows this strict: it's hard to break by
accident. If you try to import a database driver into core, or reach past the API
straight into the data layer, the build stops you before a human ever sees the PR. For
a newcomer, that's not a barrier — it's a safety net. The structure tells you where
your code goes, and catches you when you guess wrong.
Ummah Library is AGPL-3.0, owned by no one, and built as a Sadaqah Jariyah — an
ongoing charity. If you want to see how the pieces fit before you touch anything, the
best next reads are ARCHITECTURE.md (this map, in more
detail) and AGENTS.md (a decision table for where does this code
go?). When you're ready, CONTRIBUTING.md is the front door.
Next up: the one rule that makes all of this hold.
Ummah Library is open source. If this post left you with an opinion about the boundary, or you’re looking for a good first issue, we’d welcome the pull request.