PregnancyPal: building health software when the data is…
Week-by-week tracking, vitals, fertility and a community — on a free tier, with no cloud functions. What health data changes about every architectural…
PregnancyPal is a maternal wellness companion: week-by-week pregnancy tracking, nutrition, prenatal exercise, daily vitals and symptom and mood logging, period and fertility tracking, appointments, a community and a blog. It ships as a web app, a Capacitor Android app and a browser extension.
Technically it is the same stack I use everywhere. What is different is that every decision has a second question attached to it, and the second question is usually the one that matters.
Health data changes what "good enough" means
In most products, a bug that shows one user's data to another is embarrassing. Here it is a fertility log. The severity ceiling is completely different, and it changes three things:
- The rules are the product. Not a layer over it. Every collection is owner-scoped, and every list query carries the filter its rule depends on so the engine can prove the query is safe before it runs.
- Verification is adversarial. Every screen gets exercised by a second account that should not be able to see it. A feature verified only as its owner is a feature verified only on the branch that always passes.
- Deleting means deleting. A user who removes an entry expects it gone, not flagged.
Client-side processing turned out to be the privacy feature
The architecture is strictly zero-cost: client logic, the Firebase free tier, and my own platform for uploads. No cloud functions, no cloud storage.
That constraint was financial. Its effect was privacy. Cycle predictions, fertile-window estimates and the health report are all computed in the browser from data the user already has, which means that computation never leaves the device and there is no server-side log of it to secure, subpoena or leak.
I did not design it that way for privacy reasons and I am not going to pretend I did. But it is a real property of the result, and it is the strongest privacy argument the product has: the safest place to process someone's fertility data is on their own phone.
The PDF export is the feature people actually use
The most-valued feature is the one that lets someone hand a doctor a printed summary. That was a late addition and it reframed the product for me — the app is not the destination, the appointment is.
Generated in the browser, so the report is assembled from data that never goes anywhere. And it taught me something about health UX: a doctor has four minutes. A report that is comprehensive is a report that gets skimmed. Ruthless summarising, with the raw log behind it, beats completeness.
Week-by-week content is a data problem pretending to be a content problem
Forty weeks of guidance sounds like a content task. It is a modelling task, because pregnancy weeks are computed from a due date that can be revised, and revising it must move everything consistently — content, appointments, milestones — without touching a single logged vital, which is dated in real time rather than in pregnancy time.
Two clocks in one product. Getting that wrong is how a user's logged symptoms end up attached to the wrong week after a due-date change, and it is very hard to notice, because every individual value still looks plausible.
Prerendering, because health searches happen before an install
Somebody searching "what happens in week 19" is not in the app. They are in a search result or in an AI answer, and neither of those runs JavaScript reliably. So the build emits real static HTML per route, with the content in it, and the app hydrates over it for people who arrive with a browser.
This is the single highest-leverage thing I have done for discovery on any of these products, and it costs a post-build step.
The theme customiser is not decoration here
A full appearance customiser — dark mode included — for signed-out visitors as well as signed-in ones, with the choice applied before first paint so there is no flash of the wrong theme.
In a health app used at 3am by somebody who is not sleeping, dark mode is not a preference, it is the difference between using the app in bed and not. And a flash of white before the dark theme lands is precisely the moment it matters most.
Offline is a requirement here, not a feature
People log symptoms in waiting rooms, in hospital corridors and at three in the morning, which are three of the places phone signal is worst. An app that loses an entry because the network was down is not a minor inconvenience in this category — it is the moment somebody stops trusting it with anything.
So writes are optimistic and queued. The entry appears immediately, is persisted locally, and syncs when there is a connection. The interface says which state it is in rather than pretending, because a silent queue is its own kind of lie: a user who cannot tell whether something saved will save it twice.
The hard part is not the queue, it is conflict. Two devices, both offline, both logging. My resolution is deliberately unclever: entries are additive and timestamped, so both survive and neither overwrites the other. Duplicates are a much better failure than a lost entry, and they are visible enough that a person can remove one.
Notifications, and why most of them are wrong
The obvious feature is a daily reminder to log. The version that gets turned off inside a week is the one that fires at nine every morning regardless of whether you already logged at seven.
What works is reminding only when there is something outstanding, at a time the person chose, and stopping entirely if it has been ignored several times running. That last rule is the one nobody builds, and it is the difference between a notification the user tolerates and one that costs you the permission permanently — because a person who disables notifications for your app disables them for the appointment reminders too.
Android also wants the notification permission requested behind a deliberate opt-in with an explanation, and every permission in the merged manifest justified in the data-safety form. In a health category, a vague declaration is not a slow review. It is a rejection.
What I would tell someone building in this space
Write down what you will not collect, before you build. It is much easier to decide that you do not want a piece of data than to remove it once a feature depends on it.
And be exact in the store listing and the privacy policy — every permission in the merged Android manifest, including ones a plugin injected without asking, needs a justification a reviewer can read. In this category, a vague data-safety declaration is not a slow review. It is a rejection.
https://aoneahsan.com/blog/project-pregnancypal-health-tech