
Mobile Anwendung
Eine ausschließlich mobile, local-first arbeitende Anwendung, die für jede Schwangerschaftswoche religiöse und wissenschaftliche Inhalte mit Quellenangaben bereitstellt. Die Inhalte kommen mit der App und werden in einer einzigen Transaktion in das SQLite des Geräts geschrieben; der Kernablauf funktioniert ohne Verbindung, und es werden keine personenbezogenen Daten an einen Server gesendet.
Die Inhalte dieser Anwendung sind in zweierlei Hinsicht heikel: Die eine Seite ist religiöse Referenz, die andere Gesundheitsinformation. In beiden Fällen unterscheidet sich der Preis eines falschen Satzes vom Preis eines Softwarefehlers — die Nutzerin handelt in der Annahme, die Information sei richtig. Das eigentliche technische Problem ist deshalb nicht der Bildschirm, sondern der Weg des Inhalts in ein Release: Es brauchte einen Mechanismus, der ungeprüfte Texte davon abhält, in eine Version zu rutschen. Der zweite Punkt ist der Datenschutz: Schwangerschaftsdaten gehören zu den sensibelsten Daten überhaupt. "Auf dem Server halten, später verschlüsseln" ist hier die falsche Antwort; die richtige lautet, sie gar nicht erst zu erheben.
Ein Nutzertyp, aber wechselnde Nutzungsbedingungen: Die App wird über den Tag in kurzen Abständen geöffnet, meist zu Hause, manchmal ohne Verbindung und höchstwahrscheinlich auf dem einen Gerät, auf dem sie installiert wurde. Diese drei Bedingungen machen Startgeschwindigkeit und Offline-Betrieb zur Pflicht statt zur Vorliebe. Es gibt eine zweite Partei, aber nicht in der App: die Person, die die Inhalte prüft. Ihre Entscheidung steht im Repository als Feld.
Einzelner Entwickler. Inhaltsschema und Prüfpipeline, Gerätedatenbank und Importlogik, Fachlogik der Schwangerschaftswoche, Oberfläche und Release-Tor.
Das System ist serverlos: Es gibt keine API, mit der die App spricht. Die Inhalte liegen als wochenweise JSON-Dateien im Repository und sind durch ein Zod-Schema definiert; beim Paketieren wandern sie in das Binary. Beim ersten Start werden sie in die SQLite-Datenbank des Geräts importiert. Der Import trägt drei Eigenschaften zugleich: versioniert — eine Einheit wird nur überschrieben, wenn ihre Inhaltsversion steigt; idempotent — ein zweiter Lauf desselben Imports ändert nichts; und in einer einzigen Transaktion — er hinterlässt keinen halbfertigen Zustand. Große Importe werden in Stücke geteilt, damit die Oberfläche nicht blockiert; jedes Stück läuft in einer eigenen Transaktion, und zwischen den Stücken wird die Kontrolle abgegeben. Die Schichten sind getrennt: reine Fachfunktionen (Wochenberechnung, Wochenzugriff), Datenbankschicht, Inhaltsschema und Routen. Die Wochenberechnung ist zeitzonenunabhängig — da sie über eine Tagesdifferenz erfolgt, springt die Woche beim Zonenwechsel des Geräts nicht.
Schwangerschaftsdaten sind sensibel; nicht erhobene Daten können nicht abfließen. Da die Inhalte ohnehin mit der App kommen, wäre die einzige Aufgabe eines Servers die Synchronisation des persönlichen Zustands — und der Preis dieser einen Funktion sind ein Kontosystem, eine Datenschutzpflicht und laufende Betriebskosten. Dieser Tausch wurde abgelehnt.
KompromissWechselt jemand das Gerät, wandert der Fortschritt nicht mit; wird die App gelöscht, sind die Daten weg — es gibt keine Sicherung. Auch Inhaltskorrekturen kommen nicht vom Server: Damit ein Tippfehler die Nutzerin erreicht, braucht es ein neues Store-Release.
Die App kann den Import bei jedem Start aufrufen, und derselbe Pfad läuft nach einem Update. Wie oft der Import läuft, darf das Ergebnis also nicht verändern. Verglichen wird über die Inhaltsversion: Ist sie nicht gestiegen, bleibt die Zeile unangetastet. Bei einem Update werden nur geänderte Einheiten geschrieben.
KompromissDie Garantie hängt daran, dass die Inhaltsversion korrekt erhöht wird; einen Text zu ändern und die Version zu vergessen, heißt, dass die Änderung das Gerät nie erreicht — ein stiller Fehler. Nichts erzwingt die Erhöhung.
Eine Schemaprüfung sagt, dass ein Feld gefüllt ist, nicht, dass es richtig ist. Bei religiösen und medizinischen Inhalten zählt das Zweite. Deshalb trägt jeder Inhaltsblock einen Prüfstatus, und das vor einem Release laufende Tor listet die Dateien auf und endet mit einem Fehlercode, wenn es einen ungeprüften Block findet — der Build stoppt.
KompromissDas Tor sieht nur den Wert eines Feldes; ob die Freigabe selbst zutrifft, hängt an einem Menschen. Einen versehentlich freigegebenen Text fängt nichts ab, nur nie geprüften Text. Außerdem lässt sich während der Arbeit an Entwürfen kein Release bauen.
Sowohl auf religiöser als auch auf wissenschaftlicher Seite zählt die Quelle einer Aussage so viel wie der Text selbst. Ist die Quelle als Schemafeld statt als Gewohnheit definiert, kann Text ohne Quelle nicht ins Repository. Ein eigenes Kommando prüft, ob die Links noch auflösen.
KompromissDie Linkprüfung geht ins Netz und hängt von der Erreichbarkeit externer Seiten ab; ein kurzer Ausfall lässt die Prüfung fehlschlagen. Deshalb ist sie nicht Teil des Release-Tors, sondern ein separat ausgeführtes Kommando.
Expo erlaubt es einer einzelnen Person, für beide Plattformen zu liefern, und ist in einem Umfang ohne native Module der günstigste Weg. Als Gerätedatenbank fiel die Wahl auf SQLite, weil die Inhalte relational und abfragbar sein müssen; in einem Schlüssel-Wert-Speicher lässt sich keine Abfrage über Woche, Schicht und Sprache schreiben. Die synchrone Datenbankschnittstelle hält die Lesepfade ohne asynchrone Zustandsverwaltung einfach. Zod dient als Inhaltsschema und als Typquelle — ein Schema, zwei Aufgaben, wie bei MedAtlas. i18next lässt beide Sprachen mit den Inhalten ausliefern; auch Übersetzungen sind eingebettet und werden nicht aus dem Netz geholt.
Die Angriffsfläche ist bewusst schmal: Es gibt keinen Server, mit dem die App spricht, kein Kontosystem und keine Authentifizierungsschicht. Der persönliche Zustand — Woche, Fortschritt, Einstellungen — liegt nur in der Gerätedatenbank und wird nirgendwohin gesendet. Da die Inhalte mit der App kommen, gibt es zur Laufzeit auch keine heruntergeladene Nutzlast, die geprüft werden müsste. Das einzige verbleibende Risiko sind die Inhalte selbst, und die schützt das Release-Tor: Ungeprüfte Inhalte kommen nicht in eine Version.
Dies ist eine mobile Anwendung; eine Seitengeschwindigkeitsmessung ist bedeutungslos, und da keine gespeicherte Messung der Startzeit vorliegt, steht hier keine Zahl. Strukturell gibt es zwei Entscheidungen. Erstens: Da die Inhalte mit der App kommen, wird beim ersten Start kein Download abgewartet — der Bildschirm ist auch ohne Verbindung gefüllt. Zweitens: Der Inhaltsimport ist in Stücke geteilt, damit der Haupt-Thread nicht blockiert; jedes Stück läuft in einer eigenen Transaktion, und zwischen den Stücken wird die Kontrolle abgegeben — die Oberfläche friert beim ersten Start nicht ein.
Das Release-Tor an die Inhaltsfreigabe zu binden, war die richtigste Entscheidung dieses Projekts, und ihr Ursprung liegt in MedAtlas: Dort fragte das Tor "passt es zum Schema", und für Bildungsinhalte reichte diese Frage nicht. Hier wurde daraus "wurde es geprüft". Offen bleibt die Versionsnummer selbst: Die Idempotenz des Imports hängt daran, dass die Inhaltsversion korrekt erhöht wird, und nichts erzwingt das — einen Text zu ändern und die Version zu vergessen, heißt, dass die Änderung das Gerät nie erreicht. Heute würde ich eine Prüfung schreiben, die die Inhaltsdatei hasht und mit der Version vergleicht, damit die Regel im Tor statt in der Aufmerksamkeit eines Menschen liegt. Es ist dieselbe Lehre wie bei Bergaz Operations: Solange eine Garantie in einer Gewohnheit statt in einer Prüfung steckt, vergisst sie früher oder später jemand.
Sagen Sie mir, was Sie bauen wollen; ich sage Ihnen vorab, wie lange es dauert und wo man anfängt.