Technik
Warum Field-Apps scheitern: Offline-first mobile Software bauen
Eine App, die im Büro-WLAN vorgeführt und am Baustellentor mit einem Balken 4G benutzt wird, sind zwei verschiedene Programme. Nur eines davon wurde getestet.
Das Muster ist regelmäßig genug, um es vorherzusagen. Eine Field-App wird beauftragt, kompetent gebaut, erfolgreich vorgeführt und ausgerollt. Binnen zwei Monaten ist das Feldteam zurück beim Papier, und der genannte Grund lautet, „sie hat ständig Daten verloren“. Meist hat sie keine Daten verloren — sie hat sie nicht empfangen, dann nichts gesagt, und für den, der am Tor steht, ist dieser Unterschied unsichtbar.
Sobald Feldmitarbeiter einer App nicht mehr vertrauen, hören sie auf, sie zu benutzen, und keine Menge neuer Funktionen holt sie zurück. Vertrauen ist das eigentliche Produkt, und es entscheidet sich daran, was die App bei schlechter Verbindung tut.
Warum Annahmen zur Konnektivität scheitern
Büro-WLAN ist keine schwächere Version einer Baustellenverbindung — es ist etwas anderes. Eine Baustelle hat lückenhafte statt langsame Abdeckung: volles Signal am Büro, nichts hinter dem Baukörper, und ein Captive Portal im Gebäude des Kunden, das auf jede Anfrage HTTP 200 mit einer Login-Seite im Rumpf zurückgibt.
Bei diesem letzten Fall lohnt es sich zu verweilen, denn er hebelt naive Verbindungsprüfungen aus. Das Gerät meldet ein Netz. Die Anfrage gelingt. Die Antwort ist nicht die, die verlangt wurde. Eine App, die prüft „gibt es eine Verbindung“ statt „hat der Server tatsächlich geantwortet“, glaubt bereitwillig, etwas gespeichert zu haben, was sie nicht hat.
Die andere Annahme, die scheitert, ist die Dauer. Eine App, die Synchronisation binnen Minuten erwartet, verhält sich anders als eine, die ein drei Tage offline gewesenes Gerät verkraften muss, während die Serverdaten weitergelaufen sind. Der zweite Fall ist in der Feldarbeit der Normalfall, und er verändert den Entwurf.
Offline-first heißt, der lokale Speicher ist die Wahrheit
Der Unterschied ist nicht „wir cachen manches“. In einem Offline-first-Entwurf ist die lokale Datenbank der Ort, an dem die Arbeit des Nutzers lebt, Schreibvorgänge schließen lokal ab und kehren sofort zurück, und die Synchronisation ist ein Hintergrundprozess, der später mit dem Server abgleicht. Das Netz ist eine Verbesserung, keine Voraussetzung.
Das kehrt die übliche Fehlerbehandlung um. In einer Online-first-App ist fehlendes Netz ein Fehler, mit dem der Nutzer umgehen muss. In einer Offline-first-App ist fehlendes Netz ein Normalzustand, und die Oberfläche spiegelt das — der Datensatz ist gespeichert, er ist in der Warteschlange, er geht raus, sobald es geht, und der Nutzer kann sehen, dass das stimmt.
Das Konfliktmodell bewusst wählen
Zwei Personen bearbeiten denselben Datensatz, beide offline. Was dann passiert, ist Ihr Konfliktmodell, und wenn niemand eines gewählt hat, hat der Code es zufällig gewählt — meist last-write-wins, still, und die Arbeit einer Person verschwindet.
Es gibt nur wenige gangbare Optionen, und die richtige hängt von den Daten ab.
- Last-write-wins ist für wirklich unabhängige Felder vertretbar und für Daten, bei denen leichte Veralterung harmlos ist. Für alles Finanzielle oder alles, woran ein Streit hängen könnte, ist es die falsche Wahl.
- Append-only-Ereignisprotokolle vermeiden Konflikte vollständig, weil nie an Ort und Stelle geändert wird. Anwesenheitseinträge, Materialausgaben, Lagerbewegungen und Prüfprotokolle passen alle natürlich dazu — zwei Vorarbeiter, die Anwesenheit eintragen, fügen Fakten hinzu und konkurrieren nicht darum, eine zu überschreiben.
- Feldweises Zusammenführen funktioniert, wenn ein Datensatz Abschnitte hat, die verschiedenen Rollen gehören, sodass zwei Bearbeitungen meist nicht dieselben Felder berühren.
- Explizites Auflösen — beide Versionen zeigen und nachfragen — ist richtig, wo die Daten wichtig genug sind, um jemandes Aufmerksamkeit zu verdienen, und unerträglich, wenn es häufig passiert. Braucht Ihr Modell das oft, ist das Modell falsch.
Im Feldeinsatz passt append-only auf weit mehr Fälle, als Teams erwarten, und beseitigt die schwierigste Fehlerklasse konstruktiv. Es lohnt sich, die Daten dafür umzubauen.
Die Warteschlange ist der Teil, der absolut zuverlässig sein muss
Alles, was ein Nutzer offline tut, geht in eine Warteschlange, und dort wohnt jeder ernste Fehlerfall. Die Warteschlange muss überstehen, dass das Betriebssystem die App beendet, dass das Telefon neu startet und dass die App auf eine Version aktualisiert wird, deren Datenformat sich geändert hat.
Drei Regeln machen es zuverlässig. Jede eingereihte Operation trägt eine vom Client erzeugte Kennung, damit der Server ein Duplikat erkennt — Wiederholungen sind sicher, und ohne Idempotenz erzeugen sie doppelte Datensätze. Operationen werden dort in Reihenfolge angewendet, wo die Reihenfolge zählt, denn ein Anlegen, das nach der eigenen Aktualisierung eintrifft, ist ein Datenverlustfehler. Und ein dauerhaft scheiterndes Element muss in Quarantäne und sichtbar gemacht werden, statt ewig wiederholt zu werden, sonst blockiert ein schlechter Datensatz jeden guten dahinter.
Der letzte Punkt ist der häufigste Produktionsfehler. Ein Datensatz, den der Server aus Validierungsgründen ablehnt, sitzt am Kopf der Warteschlange und versucht es erneut, während alles dahinter wartet. Das Telefon zeigt „synchronisiert“. Seit zwei Tagen wurde nichts synchronisiert.
Synchronisationsstatus sichtbar machen
Nutzer verzeihen einer App, dass sie den Server nicht erreicht. Sie verzeihen ihr nicht, dass sie ihnen nicht sagen kann, ob sie es hat. Jeder Datensatz sollte zeigen, ob er lokal gespeichert, in der Warteschlange, synchronisiert oder fehlgeschlagen ist, und es sollte einen Bildschirm geben, der sagt, wann die letzte erfolgreiche Synchronisation war und wie viele Einträge warten.
Das verändert auch den Support. „Es wurde nicht gespeichert“ lässt sich nicht beantworten. „Drei Einträge in der Warteschlange, letzte Synchronisation vor 2 Tagen, einer an einem Validierungsfehler gescheitert“ ist eine Frage mit Antwort, und die lässt sich am Telefon geben.
Hintergrundarbeit, die tatsächlich läuft
Androids Akkuoptimierung stoppt naive Hintergrundsynchronisation, und die Oberflächen der Hersteller sind aggressiver als reines Android — manche deutlich. Eine App, die auf einem Pixel zuverlässig synchronisiert, synchronisiert auf einem Telefon, dessen Hersteller Hintergrundarbeit aggressiv beendet, womöglich gar nicht, und solche Telefone sind genau in der Preisklasse verbreitet, die Feldmitarbeiter tragen.
Nutzen Sie den plattformeigenen Scheduler statt eines Timers, synchronisieren Sie opportunistisch, wenn die App im Vordergrund ist, und verlassen Sie sich nie allein auf Hintergrundarbeit für etwas, das aus Nutzersicht passiert sein muss. Testen Sie auf den günstigen Telefonen Ihrer Nutzer, nicht auf dem Gerät des Entwicklers.
Auf dem Netz testen, das sie haben
- Flugmodus für einen ganzen Arbeitstag, dann wieder verbinden und prüfen, ob alles genau einmal ankommt.
- Eine gedrosselte, verlustbehaftete Verbindung statt einer bloß langsamen — die Fehlerbilder unterscheiden sich, und Paketverlust ist das, woran Dinge zerbrechen.
- Ein Captive Portal, das 200 mit einer Login-Seite zurückgibt, um zu prüfen, ob die App die Antwort statt nur die Verbindung auswertet.
- Beenden Sie die App mitten in der Synchronisation, starten Sie das Telefon mitten in der Synchronisation neu, und prüfen Sie, ob die Warteschlange beides übersteht.
- Zwei Geräte, die offline denselben Datensatz bearbeiten, um zu prüfen, dass das Konfliktmodell das tut, was Sie entschieden haben, und nicht, was das Framework standardmäßig macht.
- Eine alte App-Version, die mit einem aktuellen Server synchronisiert, denn im Feldeinsatz hinkt immer jemand drei Versionen hinterher.
Die Zusammenfassung
Offline ist keine Funktion, die man gegen Ende ergänzt. Es bestimmt Datenmodell, Konfliktregeln, Warteschlangenentwurf und Oberfläche, und es nachzurüsten kommt einer Neuentwicklung nahe. Entscheiden Sie es zuerst, machen Sie den lokalen Speicher maßgeblich, wählen Sie das Konfliktmodell ausdrücklich, machen Sie den Synchronisationsstatus sichtbar, und testen Sie auf einer schlechten statt auf einer guten Verbindung.