Stressfrei ins neue Schuljahr dank Machine Learning

Im Zuge der Weiterentwicklung der Thalia App entstehen regelmäßig neue Features. Dabei versuchen wir bereits bestehenden Code durch Refactorings für uns, aber auch für den Kunden, zu verbessern. Nach der Pfadfinderregel hinterlässt jeder im Team die Codebasis ein bisschen besser als vorgefunden.  

Problemstellung und bisheriger Prozess

Beim Aufräumen des Android Manifest bin ich auf eine Activity (repräsentiert eine Ansicht innerhalb einer App) gestoßen, die sich mit Schulbüchern befasst. Thalia hat jedes Jahr zwischen Juni und September Aktionen für Schulartikel. Dort können Hefte, Kalender oder auch Schulbücher für den Start in das neue Schuljahr gekauft werden. Die Schüler erhalten von ihren Lehrern in der Regel eine Liste mit den benötigten Materialien am Anfang des neuen oder zum Ende des vergangenen Schuljahrs. Eine solche Liste beinhaltet gewöhnlich eine European Article Number (kurz: EAN), einen Titel und einen Preis.

Die Liste kann mithilfe der Kamera über die Thalia App fotografiert und im Anschluss per Mail an Thalia übermittelt werden. Das Foto wird mit weiteren Informationen von der App an einen E-Mail-Client auf dem Gerät übergeben und für den Bestellprozess an schulbuch@thalia.de verschickt. Mit wenigen Klicks und dem Verfassen einer E-Mail waren die benötigten Materialien für das neue Schuljahr bestellt. Der Prozess ist funktional – aber kann dieser für den Kunden noch einfacher gemacht werden?

Idee zur Verbesserung

Gemäß eines unserer Schlüsselverhalten bei Thalia versuchen wir unsere Lösungen spielerisch einfach zu gestalten. Der vorher beschriebene Prozess funktioniert, lässt aber einige Fragen offen: Können die Artikel nur per Rechnung bezahlt werden oder lässt die Bestellung per Mail auch andere Zahlungsmöglichkeiten zu? Wie wird am einfachsten kenntlich gemacht, wenn ein Artikel nicht bestellt werden soll? Wie wird verfahren, wenn das Bild unkenntlich oder abgeschnitten ist? Grundsätzlich könnten die Bücher auch vom Kunden selbst gesucht und bestellt werden. Dadurch wären die vorherigen Fragen obsolet, aber das Abtippen einer längeren Liste erscheint weniger praktisch als die Aufnahme dieser mit der Kamera. An der Stelle kam die neue Idee ins Spiel: Machine Learning.

Machine Learning oder maschinelles Lernen ist ein Teilgebiet der Informatik und erkennt durch Muster und Training von Algorithmen Muster in Datensätzen. Auf unseren Anwendungsfall übertragen ist der Text auf dem Zettel, der über das Foto aufgenommen wird, unser Datensatz. Der Kern der Idee besteht darin, den Text auf dem Zettel für eine Suche in unserem Sortiment auszulesen. Prädestiniert im Datensatz sind die EANs. Diese sind eindeutig und würden, im Gegensatz zur manuellen Suche (oder Suche über eine Schnittstelle) nach einem Titel, mit Sicherheit den gewünschten Artikel auffinden.

Für die Erkennung von Text auf Bildern stellt Google mit dem Machine Learning Kit (ML Kit) eine Bibliothek für Android und iOS zur Verfügung[1]. Das versprochene Ergebnis nach Analyse eines Bildes ist ein Objekt vom Typ FirebaseVisionText[2]. Dieses ist entweder leer, wenn kein Text erkannt wurde, oder enthält je nach Präsenz des Textes auf dem Bild Textblöcke, die wiederum in Textzeilen aufgeteilt sind, oder einen einzelnen Satz.

Auf den Schulzettel übertragen würden alle EANs, Titel und Preise erkannt und in einem FirebaseVisionText-Objekt bereitgestellt werden. Durch eine Filterung ließen sich dann die EANs über einen regulären Ausdruck auslesen. Diese könnten dann via API-Call gesucht und das Ergebnis in einer Liste dargestellt werden. Mit einem Klick wandern die gefundenen Artikel in den Warenkorb und der Nutzer kann selbstständig die benötigten Schulmaterialien bestellen. Die Umsetzung der beschriebenen Theorie erfolgt im nächsten Kapitel.

Implementierung Android

Die Aufnahme des Bildes wird von der Kamera App des Android OS gehandelt. Wir geben dem entsprechenden Intent eine Uniform Resource Identifier (URI) mit, unter der das Bild nach der Aufnahme gespeichert werden soll. 

Abbildung 1: Intent für die Aufnahme durch die Kamera App

Das aufgenommene Bild wird in unserer Analyzer Klasse als Bitmap erstellt und anschließend unter Zuhilfenahme der ML-Kit-Bibliothek zu einem FirebaseVisionImage konvertiert.

Abbildung 2: Konvertierung in FirebaseVisionImage

Dabei wird auch die ursprüngliche Rotation zum Bild hinzugefügt. Die Konvertierung ist erforderlich, damit die Erkennung des Textes anschließend stattfinden kann. Das vorbereitete Bild wird daraufhin mittels eines FirebaseVisionTextRecognizer analysiert und gibt uns das im Theorieteil erwähnte FirebaseVisionText-Objekt zurück.

Abbildung 3: Analyse des aufgenommenen Bildes

Aus dem gescannten Text löschen wir die Bindestriche. Teilweise werden diese auf den Schullisten mit dargestellt, sind für die Suche aber unwichtig. Der bereinigte Text wird durch den regulären Ausdruck überprüft. Dabei werden nur Strings herausgefiltert, die Zahlen zwischen null und neun enthalten und eine Länge von zehn bis 13 besitzen. Die Längen ergeben sich aus der Länge der EAN mit 13 Stellen und der Internationalen Standardbuchnummer (kurz ISBN), die zehn (ISBN-10) oder 13 (ISBN-13) Stellen besitzt. Diese kann analog zur EAN für unsere Suche genutzt werden und wird teilweise auch auf den Schullisten abgedruckt. Die für die Suche unwichtigen Bindestriche stellen den Unterschied zwischen der EAN und einer ISBN-13 dar.   

Anpassungen nach Rollout Android

Nachdem das erste Minimum Viable Product fertiggestellt und bereits in einer neuen App Version veröffentlicht wurde, haben wir noch weitere Verbesserungen vorgenommen.

Eine Maßnahme bestand darin, die ausgelesenen EANs (oder ISBNs) auf ihre Länge hin zu untersuchen.

Abbildung 4: Bereinigung der gefundenen Ergebnisse

Bei den Längen zehn und 13 können wir uns sicher sein, dass die Nummer korrekt vom Bild ausgelesen wurde. Tauchen im Ergebnis Strings mit den Längen elf oder zwölf auf, wurden entweder ein oder zwei Zeichen zu wenig erkannt (für eine ISBN-13 oder eine EAN) oder es wurden ein oder zwei Zeichen zu viel erkannt (für eine ISBN-10). Die ersten drei Stellen bei einer ISBN-13 (oder EAN) im Kontext der Schulbuchzettel bestehen aus den Zahlenfolgen „978“ (oder „979“). Dabei handelt es sich um Präfixe für das Medium Buch. Da auf den Schulbuchzetteln in der Regel nur Bücher zu finden sind und wir nicht sicher sein können, ob zu viel oder zu wenig erkannt wurde, löschen wir das Präfix im untersuchten Text. Allerdings auch nur, wenn das Präfix darauf hindeutet, dass eine oder zwei Stellen der ISBN nicht erkannt wurden. Innerhalb der drei möglichen Ziffernfolgen (ISBN-13, EAN und ISBN-10) handelt es sich bei der letzten Stelle um eine Prüfziffer. Durch ein Streichen des vorangestellten Präfixes ist die Prüfziffer nicht korrekt. Die mit dieser fehlerhaften Nummer angesprochene Schnittstelle übernimmt die Fehlerkorrektur, sodass wir keine Prüfziffer neu berechnen müssen. Bei Nummern mit weniger als zehn oder mehr als 13 Stellen hat die Analyse zu sehr gestreut und es wird keine Bereinigung vorgenommen.

Eine weitere Verbesserung haben wir bezüglich der Rotation entwickelt. Das Problem bestand darin, dass die Analyse des Textes auf dem Bild weniger Erfolg hat, wenn dieses z. B. um 90 Grad gedreht war. Das heißt, das Bild wurde z. B. im Landscape-Modus aufgenommen und die Analyse hat im Portrait-Modus ohne Rotation in diesem stattgefunden.

Abbildung 5: Rotation des Bildes für weitere Analyse

Die Lösung für das Problem besteht darin, das Bild solange zu rotieren, bis Ergebnisse gefunden werden. Wenn die Analyse ohne Ergebnis bleibt findet eine erneute Analyse mit einem um 90 Grad rotierten Bild statt. Dieser Vorgang wird so oft wiederholt, bis die Rotation das Bild einmal komplett um 360 Grad gedreht hat oder bis ein Ergebnis vorliegt.

Scannen des Schulzettels in der App

Nach der Theorie und der Beschreibung des Codes soll in Form von Screenshots das Feature in der App gezeigt werden. Als erstes erfolgt die Aufnahme des Bildes.

Abbildung 6: Aufnahme des Bildes mit der Kamera App

Anschließend wird das Bild analysiert und die Ergebnisse in einer Liste dargestellt.

Abbildung 7: Ergebnisliste nach der Analyse des aufgenommenen Bildes

Der Kunde hat die Möglichkeit, die benötigten Materialien in den Warenkorb zu legen.

Abbildung 8: Alternativoptionen bei fehlerhafter Analyse

Falls bei einem Scan etwas schiefläuft und keine Ergebnisse gefunden werden, sind Alternativen verfügbar. Eine davon ist der eingangs beschriebene Prozess mit der Versendung des Bildes per E-Mail.

Fazit und Ausblick

Der Schulbuchscanner hat den Bestellprozess für Schulmaterialien anhand einer Liste deutlich erleichtert. Für den Kunden ist kein mühsames Eingeben der EANs oder das Versenden eines Bildes erforderlich. Mit dem Scanner und der Analyse eines Fotos können alle benötigten Materialien unter Nutzung der gewünschten Bezahlmethode direkt an die Wunschadresse bestellt werden. Auch die Mitarbeiter bei Thalia profitieren. Dank des Scanners kommen deutlich weniger Anfragen bei schulbuch@thalia.de an, wodurch eine geringere Anzahl an Bestellungen für die Kunden manuell vorgenommen werden muss.

Künftig könnte die generelle Scanning-Funktionalität auch für andere Use-Cases verwendet werden. Die Erkennung von Text auf Bildern ist, wie der Schulbuchscanner gezeigt hat, möglich. Allerdings wird der Anwendungsfall hier dadurch erleichtert, dass ein Großteil des Textes durch einen regulären Ausdruck herausfilterbar ist.


[1] https://developers.google.com/ml-kit/vision/text-recognition

[2] https://firebase.google.com/docs/reference/android/com/google/firebase/ml/vision/text/FirebaseVisionText




Bereitstellung von Testdaten einmal smart

Wir, das Team AiM (Artikel im Mittelpunkt), haben mit dem Testdatentool eine Anwendung entwickelt, um anderen Teams Testartikel zur Verfügung zu stellen. Diese Anwendung möchte ich in diesem Blogartikel vorstellen.

Das Problem der Beschaffung und Bereitstellung von Testdaten

In meinen sieben Jahren als QA-ler bei der Thalia ist mir ein Problem immer wieder begegnet: Sobald Testdaten in anderen Teams liegen, nimmt deren Beschaffung viel Zeit und Energie in Anspruch. Nicht zu vergessen die Aufwände auf Seiten der Teams, die Testdaten bereitstellen.

Über viele Jahre hinweg haben wir im Team AiM für andere Teams Testartikel über individuelle SQL-Abfragen herausgesucht und über die Herausgabe der Artikel-ID oder EAN bereitgestellt. Dieser Prozess hat sich mit der Zeit als sehr ineffizient herausgestellt. Um diesen Prozess zu optimieren haben wir das Testdatentool entwickelt, über welches alle Teams eigenständig Testartikel beziehen können.

Das Testdatentool stellt zwei Dienste zur Verfügung. Zum einen bietet es eine Weboberfläche, welche die Suche nach Testartikeln über alle gängigen Browser ermöglicht. Zum anderen stellt das Testdatentool einen REST-Service bereit, den Entwicklerteams im Rahmen ihrer automatisierten Tests einbinden können, um dynamisch Testartikel zu beziehen.

Aufbau

Die Weboberfläche und der REST-Service bilden zusammen das Frontend und das Backend des Testdatentools (siehe Abbildung). Während das Frontend die Suchanfrage durch den Anwender über eine Weboberfläche entgegennimmt, führt das Backend die Suche über einen REST-Service auf der Datenbank aus, um die gefundenen Treffer im JSON-Format zurückzugeben. Die Begriffspaarungen Weboberfläche/REST-Service und Frontend/Backend werden in diesem Blogartikel synonym verwendet.

Weboberfläche

Die Weboberfläche des Testdatentools ermöglicht es dem Anwender über wenige Klicks einen passenden Testartikel zu finden (siehe Abbildung). Insgesamt bietet die Weboberfläche 23 Kategorien, die für die Suche nach einem Testartikel zur Verfügung stehen. Für jede Suche können die Marke (thalia.de, thalia.at, bol.de oder orellfuessli.ch), die Umgebung (Integrations- oder Produktivumgebung) und die Anzahl der benötigten Testartikel über entsprechende Comboboxen eingestellt werden. Einige Kategorien können zusätzlich über weitere, individuelle Filter konkretisiert werden. Die Abbildung veranschaulicht einen solchen zusätzlichen Filter für die Kategorie „FSK-Artikel“. Nachdem die Suche ausgeführt und die gefundenen Artikel in der Trefferliste angezeigt werden, kann der Anwender sich das zu Grunde liegende SQL über den Button „SQL“ anzeigen lassen. Weiter kann der Anwender über den Button „COPY EANS“ alle EANS aus der Trefferliste in den Zwischenspeicher des Betriebssystems kopieren. Über diesen können die EANS in jedes andere Programm übertragen werden. Die Buttons „COPY ARTIKEL-IDS“ und „COPY MATNRS“ verhalten sich entsprechend analog zu „COPY EANS“.

REST-Service

Der REST-Service besitzt für jede Kategorie, welche die Weboberfläche anbietet, eine REST-Ressource. Hinter jeder REST-Ressource verbirgt sich wiederum ein SQL, über das die Testartikel auf der Artikeldatenbank gesucht werden. In das SQL werden die Werte eingesetzt, die der Aufrufer über URL-Parameter mitliefert. Die gefundenen Testartikel werden in einem definierten JSON-Format zurückgeliefert.

Wir haben uns dazu entschlossen, den Funktionsumfang des REST-Service stets mit dem der Weboberfläche synchron zu halten. Denn während die Implementierung einer neuen Kategorie in der Weboberfläche ohne eine Anpassung des REST-Service nicht möglich ist, wäre es sehr wohl möglich, den REST-Service für einen automatisierten Test um eine neue Kategorie zu erweitern, ohne diese in die Weboberfläche aufzunehmen. Indem wir die Weboberfläche und den REST-Service synchron halten, stellen wir sicher, dass die Entwicklerteams die Weboberfläche als bildhafte Dokumentation des REST-Service nutzen können.

Designentscheidungen

In diesem Kapitel beschreibe ich Designentscheidungen, die wir für das Testdatentool getroffen haben.

Bei der Implementierung der Weboberfläche haben wir besonders viel Wert auf eine einfache Erweiterbarkeit gelegt, damit die Weboberfläche auch ohne tiefgreifende Vue.js-Kenntnisse von allen Entwicklern des Teams erweitert werden kann. Das nachfolgende Codebeispiel verdeutlicht, dass lediglich die Erstellung einer neuen Klasse notwendig ist, um die Weboberfläche um eine neue Kategorie zu erweitern. Nachdem diese Klasse erstellt ist, muss diese nur noch in der main.js eingetragen werden, um die Erweiterung im Frontend abzuschließen.

Die zweite Designentscheidung betrifft die Optimierung des Antwortzeitverhaltens des Backends. Von Beginn an war klar, dass einige Suchen bis zu 10 Sekunden dauern würden, da die jeweiligen SQLs sehr komplex sind. Um die Antwortzeiten zu optimieren, wurde das Backend um einen Cache inklusive Cacheaufwärmmechanismus erweitert. Hierdurch wird sichergestellt, dass das Testdatentool auch bei Langläufer-SQLs von über 10 Sekunden, in unter 500 Millisekunden antwortet. Anders ausgedrückt garantiert der Cacheaufwärmmechanismus, dass für jede Langläufer-Suche zu jedem Zeitpunkt ein gültiger Cache-Eintrag existiert und die Suche aus dem Cache bedient werden kann.

Die letzte Designentscheidung betrifft ebenfalls das Backend und besteht in der Nutzung von Swagger zur Dokumentation der REST-Ressourcen. Swagger bietet unserem Team vor allem zwei Vorteile. Zum einen spart unser Team durch Swagger Zeit, da die Dokumentation mit wenig Aufwand erstellt werden kann. Zum anderen ermöglicht Swagger eine inhaltlich ausdrucksstarke Dokumentation jeder einzelnen REST-Ressource. Dies ist besonders wichtig, da der REST-Service durch andere Teams genutzt wird und dessen Verwendung weitestgehend selbsterklärend sein soll. Die Einbindung von Swagger gestaltet sich dabei denkbar einfach. Nach der Aufnahme der Swagger-Dependency in die pom.xml und der Anreicherung der REST-Ressourcen um Metadaten zu jedem URL-Parameter, genügt eine Annotation über der REST-Service-Klasse, um Swagger einzubinden. Wie die Swagger-Dokumentation für den REST-Service aussieht ist der linken Abbildung zu entnehmen. Die rechte Abbildung zeigt die Detailansicht einer einzelnen Ressource.

Technologien

In diesem Kapitel möchte ich kurz die Technologien nennen, die für die Entwicklung des Testdatentools verwendet wurden. Weiter erläutere ich stichpunktartig die Gründe, welche für die Auswahl der Technologien ausschlaggebend waren.

Vue.js als Framework für die Weboberfläche – Vorteile:

  • Vue.js setzt auf bekannte Technologien wie JavaScript, HTML und CSS
  • Eine sehr aktive Community und eine umfassende Dokumentation mit vielen Best-Practice-Beispielen
  • Steile Lernkurve, da das Konzept hinter Vue.js leicht zu verstehen ist
  • Sehr gute Performance aufgrund einer effizienten Implementierung des virtual DOM

Spring-Boot als Framework für den REST-Service – Vorteile:

  • Unser Team hatte bereits Erfahrungen im Umgang mit Spring-Boot
  • Aufsetzen eines REST-Service ohne langwierige Konfigurationsarbeiten
  • Einfacher Datenbankzugriff durch Spring-Boot-Funktionalitäten

Erfahrungsbericht

Dieser Erfahrungsbericht soll denen helfen, die eine Anwendung im Stile des Testdatentools für ihr eigenes Team in Erwägung ziehen.

Als wir das Testdatentool eingeführt haben, war die Resonanz durch die anderen Teams durchweg positiv. Insbesondere die Weboberfläche wurde von Beginn an intensiv genutzt und überzeugt seitdem durch ihre übersichtliche und einfache Bedienung. Auf der anderen Seite hat es einige Zeit gedauert, bis der REST-Service als Testartikel-Lieferant für automatisierte Tests bei den Teams Einzug erhalten hat. Tatsächlich gibt es immer noch Teams, die auf die Nutzung des REST-Service im Rahmen ihrer automatisierten Tests verzichten und stattdessen darauf zurückgreifen, Testartikel statisch in ihre Tests einzubinden. Dies ist für viele Teams völlig ausreichend, wenn Testartikel in den Tests nur eine untergeordnete Rolle spielen.

Das Testdatentool hat unserem Team dabei geholfen, die Anzahl der Anfragen, die sich um die Bereitstellung von Testartikeln drehen, um den Faktor vier zu reduzieren. Hierdurch hat sich die Entwicklungszeit des Testdatentools nach kurzer Zeit bezahlt gemacht. Nicht zu vergessen, dass es auch auf Seiten der Teams, die nach Testartikeln suchen, zu Zeiteinsparungen kommt. Abschließend kann ich mit Überzeugung sagen, dass sich dessen Einführung für uns und alle Beteiligten gelohnt hat.




Wie schnell sind eigentlich eCommerce Webseiten?

Bei einer Webseite kommt es auf das Nutzungserlebnis  / die User Experience an. Es sollen die richtigen Produkte, idealerweise mit schönen großen Bildern angezeigt werden, die Seite soll personalisiert sein, aber die Webseite muss auch schnell laden. Diesen Balanceakt zwischen guten Inhalten und einer schnellen Seite sorgt dafür, dass das Stöbern auf der Seite Spaß macht und das der Besucher idealerweise zu einem Kunden wird.  Nur wie messe ich eigentlich „Ladezeit“?

Für das Messen von „Ladezeiten“ gibt es zwei Möglichkeiten:

Synthetische Ladezeiten Messung:
Bei der synthetischen Messung wird die Webseite in einer „Labor Umgebung“ gemessen. Die Seite wird immer vom gleichen Device, mit der gleichen Internetleitung in einem festen Browser getestet.
Da der Client stabil ist, ist es klar, dass es an der Webseite liegt, wenn sich die Ladezeit verändert.
Bei der Auswahl des Clients / der Testumgebungen muss versucht werden tatsächlichen Besucher der Webseite zu simulieren (Verbindung, Gerät etc.) da ansonsten die Aussagekraft nur begrenzt ist. Mit dieser synthetischen Messung kann auch jede beliebige Webseite gemessen werden und damit ein Benchmark erstellen werden. 

Real User Monitoring (RUM):
Um die tatsächliche Ladezeit der Besucher zu messen, wird ein Real User Monitoring benötigtDamit wird, innerhalb des Browsers des Besuchers, gemessen wie die Ladezeit der Webseite ist. 
Die Varianz ist größer, da die Besucher mit mehr Browsern, Devices etc. reinkommen als in einer Laborumgebung aufbaut werden kann.
Für das Messen von Real User Monitoring gibt es viele Tools. Am verbreitetsten sind Google AnalyticsNew Relic und boomerang.js.
Im Vergleich zur synthetischen Messung muss in der Regel ein zusätzliches JavaScript ausgeführt werden, welches dann einen Impact auf den Besucher hat.
Die Zahlen im Vergleich zu anderen Webseiten zu setzen, ist schwierig, damit ist die Aussagekraft der Messung schwierig.  
Wenn jetzt 75% der Besucher einen First contentful paint  (FCP) von unter 1,5 Sekunden haben, ist das für einen Webshop gut oder schlecht?  

Das ist die Aufgabenstellung: Auf Basis von Real User Monitoring ein Marktvergleich von eCommerce Händlern in Deutschland.  Mit was für einer Toolchain können solche Daten erhoben und visualisiert werden? 

Chrome User Experience Report (CrUX)

Im Januar 2020 hat der Chrome ein Marktanteil von ca 44%. Sollte der Nutzer nicht widersprochen haben Nutzer Statistiken zu erheben, werden diese Daten automatisch an Google übermittelt und gespeichert. Google stellt diese Daten anonymisiert in seinemChrome User Experience Report (CrUX)zur Verfügung.  Dafür werden die Daten in BigQuery (Google Clouds Data Warehouse) gespeichert und öffentlich verfügbar gemacht. 

Dieser Report wird monatlich erstellt und beinhaltet eine Reihe an Performance Daten.  

Was kann damit ausgewertet werden?

Mithilfe von BigQuery können historische Daten (ab Oktober 2017) auf Domainsbasis ausgewertet werden.
Die KPIs sind sowohl Endgeräte-spezifisch (Mobil, Desktop) als auch Verbindungs-spezifisch (4G, 3G etc.) 

SELECT
    SUM(bin.density) AS density
FROM
    `chrome-ux-report.country_de.202001`,
    UNNEST(first_contentful_paint.histogram.bin) AS bin
WHERE
    bin.end <= 1500 AND
    origin = 'https://www.thalia.de'
    AND form_factor.name = "phone"
ORDER BY
    density DESC

-- Ergebnis = 61%

Dieser Abfrage sagt aus, dass 61% der deutschen Mobilen Chrome User ein First Contentful Paint von <= 1,5 sec bei thalia.de haben. Die Query lässt sich dann auch leicht abwandeln, um Informationen für andere Domains oder auch Ländern zu bekommen.

Damit können auf Monatsbasis die Werte ermittelt werden.

Als Alternative zu BigQuery stehen auch fertige Lösungen wie https://crux.run zur Verfügung.

Es gibt allerdings noch zwei Themen, die etwas stören:

  1. Daten werden auf Monatsbasis aggregiert. Monatlich eine „Überraschung“ zu bekommen ist doof. Idealerweise soll auf tägliche oder wöchentliche Basis ein Trend ermitteln werden können.
  2. Es werden nur aggregierte Daten für eine Domain angezeigt nicht für einzelne Seiten.
    Aber unterschiedliche Seitentypen haben unterschiedliche Ladezeiten. Eine Startseite hat andere Herausforderungen als eine Artikeldetailseite. Idealerweise werden einzelne Seitentypen gemessen und diese miteinander verglichen. 

PageSpeed Insights (PSI) API für detailliertere RUM Messungen

PageSpeed Insights ist eine Toolsuite, welches mehrere Funktionalitäten in einer Oberfläche anbietet.

  • Lighthouse  als synthetisches Messtool, welches KPIs ermittelt und auf Best Practices überprüft. Das Bekannteste ist der Lighthouse Score. Ein Wert, der aussagt in wie weit Best Practices eingehalten werden.
    Lighthouse ist ansonsten auch innerhalb von Chrome & Firefox enthalten.
  • Tagesaktuelle Crux Daten PSI stellt tagesaktuelle Crux Daten zur Verfügung. Die Daten werden täglich aktualisiert, beinhalten aber den Durchschnitt aus den letzten 30 Tagen rollierend
  • Ausgewählte Crux Daten für einzelne Seiten im BigQuery und damit öffentlich verfügbar, sind die Daten für eine komplette Domain. Innerhalb von PSI werden zusätzlich auch noch die Daten für einzelne Seiten dargestellt.
    Bedingung ist, dass die Seite genug Traffic hat und somit eine anonymisierte Auswertung möglich ist.
  • PSI stellt nur ausgewählte Daten zur Verfügung.
    PSI fokussiert sich auf First Contentful Paint (FCP) und First Input Delay (FID) und stellt nur diese Daten zur Verfügung.
    Weitere KPIs (TTFB, DCL, etc.) werden aktuell nicht angezeigt
  • Eine Bewertung der KPIs: PSI gruppiert die KPI Werte in drei Bereiche schnell, durchschnittlich, langsam. Dabei gelten aktuell folgende Grenzwerte, unabhängig vom Gerätetyp:
Schnell Durchschnittlich Langsam
FCP [0–1.000 ms] [1.000–2.500 ms] über 2.500 ms
FID [0–50 ms] [50–250 ms] über 250 ms
  • Zusätzlich gibt PSI für den FCP den 75% Quantil und bei FID den 95% Quantil zurück und stuft die Seite nach diesem Wert in schnell, durchschnittlich oder langsam ein. 

Lesebeispiel: Wenn die Registrierungsseite für Privatkunden in PSI bewertet wird, …:  https://developers.google.com/speed/pagespeed/insights/?hl=de&url=https%3A%2F%2Fwww.thalia.de%2Fregistrierung%2Fprivatkunde&tab=mobile

… dann hat die Seite auf mobilen Endgeräten: 

  • Durchschnittliche (63 Punkte) Lighthouse Bewertung.
  • 88% der Besucher eine schnelle FCP Zeit hatten (< 1.000 ms)
    und das der 75% Quantil bei 800 ms liegt.
    Leider haben 3% eine FCP von über 2,5 Sekunden.
  • 98% eine schnelle FID (<50ms) hatten und das kein FID über 250ms liegt. 
  • die Seite sowohl in FCP als auch FID „schnell“ ist (grüner Punkt) und damit die Seite insgesamt „schnell“ ist. 

Damit jetzt nicht jedes Mal die Webseite aufgerufen werden muss, stellt PSI stellt auch eine kostenfreie API zur Verfügung. 

Monitoring & Dashboard Lösung

Was soll erreicht werden?
Ein Report der die PCI & FID für unterschiedliche Domains anzeigt.

Was muss dafür gemacht werden? 

  • die Rest API aufrufen
  • Daten speichern
  • täglich / wöchentlich aktualisieren
  • Daten visualisieren
  • für den Fehlerfall ein Monitoring / Jobsteuerung aufsetzen

Von der Datenmenge her ist es eine CSV Datei, die auch in Excel / Spreadsheet gespeichert werden kann.
Warum nicht also per Google Spreadsheet realisieren?

AppScript

Innerhalb von Google Spreadsheet gibt es eine Chrome V8 Laufzeitumgebung, die sich  Apps Script nennt.  Scripte können veröffentlicht werden und es existiert ein Ökosystem für Entwicklung, Jobsteuerung / Trigger, Monitoring, Fehlermails etc. 
Es gibt Limitierungen, so kann z.B. weder asynchron gearbeitet werden, noch gibt es Quotas, die je nach GSuite modell unterschieden werden. Für diesen Benchmark reicht jedoch die kostenfreie Version aus. 

Mit folgendem Code Snippet wird PSI aufgerufen und in einer Tabelle gespeichert.  

function callPageSpeedV5(url, strategy) {
  
  var pageSpeedUrl = 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=' + encodeURIComponent(url) + '&key=' + pageSpeedApiKey + '&strategy=' + strategy;
  console.info(pageSpeedUrl);
  try{
    var response = UrlFetchApp.fetch(pageSpeedUrl);
    var json = response.getContentText();
 
    return JSON.parse(json);
  
    }catch (e) {
        console.error('callPageSpeedV5 Error on ' + url + ":" + e);
    }
                    
}


function writeJSONtoSheet(result, strategie) {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("data");
  
    sheet.appendRow([Utilities.formatDate(new Date(), 'GMT', 'yyyy-MM-dd'),  
                   result.id, 
                   "FCP", 
                   result.metrics.FIRST_CONTENTFUL_PAINT_MS.percentile, 
                   result.metrics.FIRST_CONTENTFUL_PAINT_MS.distributions[0].proportion,
                   result.metrics.FIRST_CONTENTFUL_PAINT_MS.distributions[1].proportion,
                   result.metrics.FIRST_CONTENTFUL_PAINT_MS.distributions[2].proportion, 
                   result.metrics.FIRST_CONTENTFUL_PAINT_MS.category,
                   result.metrics.FIRST_INPUT_DELAY_MS.percentile, 
                   result.metrics.FIRST_INPUT_DELAY_MS.distributions[0].proportion,
                   result.metrics.FIRST_INPUT_DELAY_MS.distributions[1].proportion,
                   result.metrics.FIRST_INPUT_DELAY_MS.distributions[2].proportion,
                   result.metrics.FIRST_INPUT_DELAY_MS.category, 
                   strategie
                   
   ]);  


}

Datastudio

Bisher ist die Datenablage und die Datenaktualisierung geregelt. Jetzt müssen die Daten noch visualisiert und verfügbar gemacht werden.
Eine Lösung hierfür ist Data Studio. Data Studio ist ein Cloud Tool, mit dem unterschiedliche Datenquellen angebunden und visualisiert werden können. Es gibt Daten Connectoren sowohl für Services (z.B. Facebook, Google Analytics, Spreadsheets) als auch Technologien (MySQL, PostgreSQL, BigQuery etc.). Insgesamt sind es ca. 200 Daten Connectoren, die sowohl kostenlos, kommerziell oder open Source sein können. Wenn der gewünschte Daten Connector nicht vorhanden ist, kann auch ein eigener entwickelt werden.

Damit existiert eine Möglichkeit mit der die Daten automatisch aktualisiert und visualisiert werden. 

eCommerce Website Performance Dashboard

Im Report sind ca. 130 eCommerce Händler Vertreten. Für diese Händler wird auf täglicher Basis die PSI Daten ermittelt und visualisiert. Dabei wird sowohl nach Device unterschieden und es werden die Daten für die Startseite und die komplette Domain ermittelt. 
Glückwunsch gehen nach Ibbenbüren wo https://www.musik-produktiv.de die stand heuteeinzige Domain haben, die Mobil sowohl in FCP als auch FID „fast“ ist. 






SonarQube Integration in einem Android Projekt

Um eine bessere Code Qualität an unseren Softwareprodukten zu gewährleisten haben wir uns im Unternehmen entschlossen eine statische Code Analyse einzuführen.
Ein besonderer Augenmerk liegt hierbei auf der Code Coverage und das Einhalten von zuvor festgelegten Programmier-Richtlinien.

In diesem Artikel geht es um das Erstellen+Anzeigen von Testreports auf einem SonarQube Server mittels Jenkins, um das Herunterladen+Anzeigen dieser in Android Studio und um das Anzeigen von lokalen, neuen SonarQube Issues. Desweiteren wird noch kurz auf die SonarQube-Benutzer-Oberfläche eingegangen.


SonarQube ist ein Tool für die statische Code Analyse. Hierfür werden zuvor erstellte Test Reports von SonarQube eingelesen und nach bestimmten Richtlinien und Regeln ausgewertet.

Vorbereitung des lokalen Projekts

Zuerst muss das Android-Projekt so konfiguriert werden, dass es Jacoco Test Reports erstellt. Dafür wird eine jacoco.gradle Datei erstellt:

In dieser Datei wird der Gradle-Task zur Erstellung des Test-Reports angelegt und die entsprechenden Pfade für die Source- und Class Dateien für die Java- und Kotlin Klassen angegeben.

In der build.gradle Datei des Moduls muss dann noch die jacoco.gradle Datei hinzugefügt werden.

Zum Schluss müssen noch einige Konfigurations-Einstellungen für Sonar in den gradle.properties angegeben werden.

Nun können mit den folgenden Gradle-Tasks die Test-Reports erstellt werden.

  1. Build
    – baut das Projekt und generiert die benötigten Class-Datein
  2. testDebugUnitTest
    – führt die Tests aus und erstellt einen jacoco Test-Report
    (modul/build/jacoco/testDebugUnitTest.exec)
  3. jacocoTestReport
    – erstellt die Test-Reports im HTML Format
    (modul/build/reports/jacocoTestReport/)

Es
kann auch nur der jacocoTestReport-Task ausgeführt werden, da dieser
eine Abhängigkeit zu den anderen Tasks beinhaltet. Bei einem cleanen
Projekt benötigt dies aber mehr Zeit.

Nach dem Ausführen der Gradle-Tasks liegen die Reports im Modul-Ordner unter build/reports/jacoco/TestReports im HTML-Format vor und können im Browser angezeigt werden.

Vorbereitung des Jenkins-Servers

Um sich die Reports im Sonar an zu schauen, kann man diese über den Jenkins-Server zu Sonar übertragen.
Im Jenkins muss zuerst das Sonar-Plugin installiert werden, bei dem die URL des Servers eingetragen wird und der Sonar Runner ausgewählt werden.

Die Einstellung für das Sonar-Plugin finder man unter:
Manage Jenkins -> Configure System -> SonarQube

Die Einstellung für den Sonar-Runnar befindet sich unter:
Manage Jenkins -> Global Tool Configure -> SonarQube Scanner

In dem Jenkins-Job müssen dann noch die folgenden Gradle-Tasks eingetragen werden:

Anschließend wird der SonarQube Scanner konfiguriert. Hier muss die Datei mit den Properties für Sonar angegeben werden.

Nun wird jedes Mal, wenn der Jenksins-Job für das Projekt ausgeführt wird, auch der Sonarqube Scanner ausgeführt und veröffentlicht seinen Report auf dem SonarQube-Server.

Benutzeroberfläche des Sonar-Servers

Damit Sonar die Testreports empfangen kann und diese korrekt angezeigt werden, müssen hier noch das SonarKotlin-, das SonarJava- und das Git-Plugin installiert werden.

Auf der Startseite von Sonar werden alle Projekte aufgelistet und eine Übersicht der Analyse angezeigt.

Klickt man auf ein Projekt, werden detailliertere und zusätzlich Informationen angezeigt.
Man kann die Analyse in 2 Kategorien einteilen. Das sind zum Einen Messungen (Code Coverage, Duplication) die SonarQube anhand des Codes durchführt und zum Anderen Issues (Bugs, Vulnerabilities, Code Smells), die durch Verletzung von zuvor festgelegten Code Richtlinien auftreten. Eine zusätzliche Richtlinie kann man für ein sogenanntes Quality Gate festlegen. Dies kann zum Beispiel eine Mindestanforderung von der Code Coverage des neu entwickelten Codes sein.

Über die einzelnen Analyse-Kategorien gelangt man zu einer detaillierten Ansicht der einzelnen Issues oder Messungen.

Isseus (Bugs, Vulnerabilities, Code Smells)
Messungen (Code Coverage, Duplications)

SonarQube Community Plugin

Für
Android-Studio gibt es das SonarQube Community Plugin, welches 2
Funktionen mit sich bringt:

  1. Anzeigen von vorhandenen Issues vom Sonar-Server
  2. Anzeigen von neuen Issues mittels einer lokalen Projekt-Analyse

Vorhandene Issues vom Sonar-Server

Um sich die vorhandenen Issues vom Server herunterzuladen, muss das Plugin zuerst konfiguriert werden. Hierzu ruft man dieses in den Einstellungen von Android-Studio auf und trägt den Sonar-Server ein. Anschließend wählt man das Projekt in dem Abschnitt Resourcen aus.

Unter dem Reiter Analyse -> Inspect Code muss in dem Inspection Profile SonarQube ausgewählt werden. Nun werden bei der Code Inspection die Sonar-Issues vom Server heruntergeladen und in der IDE angezeigt.

Lokale Projekt-Analyse mit dem Sonar-Gradle-Plugin

Um das lokale Projekt nach Sonar-Issues zu analysieren muss zunächst das SonarQube-Script konfiguriert werden. Hier muss der Sonar-Gradle-Task und der Pfad zum sonar-report eingestellt werden. Der Sonar-Report ist vor dem ersten Ausführen noch nicht vorhanden, da er erst durch den Gradle-Task erstellt wird.

Als nächster Schritt muss lokal SonarQube konfiguriert werden. Hierzu wird zuerst in der build.gradle Datei des Projekts sonarQube als Dependencie hinzugefügt.

In der build.gradle Datei des Moduls wird SonarQube dann applied und konfiguriert.

Nachdem SonarQube konfiguriert wurde, kann das Projekt unter Analyze -> Inspect Code analysiert werden. Hierzu muss nur noch das Sonar-Profile (mit Sonar Issues + new Sonar Issues) ausgewählt und mit OK bestätigt werden.

Nach der Analyse werden die Sonar-Issues vom Server und die aus der lokalen Analyse unten im Reiter Inspection Results angezeigt.

Neu gefundenen lokalen SonarQube Issues werden zusätzlich noch in einem kleinen Popup unten rechts von Android Studio angezeigt.

Nach der Einführung von SonarQube in unser Android-Projekt haben wir eine zentrale Anlaufstelle für unsere Code Qualität und kontrollieren regelmäßig diese in der SonarQube-Benutzer-Oberfläche.
Desweiteren bekommen wir über Jenkins ein schnelles Feedback ob unser Quality Gate eingehalten wurde.
Mit dem Community-Plugin laden wir uns die SonarQube-Issues vom Server herunter und beheben diese direkt in Android Studio. Bevor wir ein Feature pushen, kontrollieren wir mittels des Plugins im Vorfeld das Einhalten der Programmier Richtlinien und können neu aufgetretene Issues direkt in Android Studio beheben.




Hackathon 2019 in Berlin

Unseren 3. Hackathon haben wir im Juni 2019 zusammen mit unserem B2B-Partner Douglas ausgerichtet. An zwei Tagen pitchten wir mit 25 Teilnehmern Ideen, probierten neue Methoden und Techniken aus und teilten unser Wissen. Das alles gut versorgt, mit jeder Menge Spass und Motivation. Mit dem Thema Personalisierung haben wir den Hackathon dieses Mal inhaltlich geschärft. Das Publikum hat nach den Abschluss-Präsentationen zwei Projektideen prämiert und die Gewinner durften sich über tolle Preise freuen. Über die Themen und Eindrücke möchten wir euch über unseren Techblog teilhaben lassen.

Thalia B2B-Entwicklung in Berlin

In Berlin entwickeln wir seit 2014 für B2B-Partner digitale Produkte im Retail-Umfeld. In der kontinuierlichen Produktentwicklung sind uns agile Arbeitsweisen und stabile Teams sehr wichtig. Unser Schwerpunkt liegt in der Frontend-Entwicklung für mobile Geräte auf Basis von Android und iOS.

Personalisierung – Unser Schwerpunktthema

Mit unseren Teams in Berlin entwickeln wir seit mehreren Jahren erfolgreich Apps für unseren B2B-Partner Douglas.  In Absprache mit Douglas haben wir den inhaltlichen Schwerpunkt auf das Thema Personalisierung gelegt und uns überlegt, wie wir diesbezüglich Projektideen generieren können. Die Teilnehmer haben wir vorab mit einer Lightning-Demo Session eingestimmt, in der aktuelle Apps bzw. Websites vorgestellt wurden, die inspirierende Personalisierungsfunktionen anbieten.

Lightning Demos

Eine Methode mit der sich ein Team mit Produkten und Services für die Konzeptphase inspiriert. Wird in der Methodik „Design Sprint“ angewendet.

Ablauf
– Jeder Teilnehmer recherchiert selbst 2-3 inspirierende Beispiele
– Jeder Teilnehmer präsentiert die Beispiele
– Die Kernideen werden auf Klebezetteln geschrieben und an ein Board angebracht

Am Ende hat das Team 10-20 Ideen zu dem Thema gesammelt. Es geht darum von allen Teilnehmern die besten persönlichen Ideen aufzunehmen und dabei nicht zu diskutieren. 

https://www.sessionlab.com/methods/lightning-demos
https://www.thesprintbook.com

Die Projekte

Am Morgen des ersten 1. Tages stellten die Teilnehmer 6 Projektideen vor. Neben 5 technischen Vorschlägen aus dem Mobile-Umfeld hatten wir mit dem Design-Sprint-Projekt auch ein Methode aus dem UX-Umfeld am Start. So starteten wir nach dem Pitch mit 4 Teams in die Projekte.

Team Mini Design Sprint – Solve Big Problem And Test New Ideas

Das Thema Personalisierung sind wir methodisch mit einem Mini Design-Sprint angegangen. Ein Design-Sprint ist Konzeptionsmethode für Produkte, die innerhalb von 5 Tagen mit dem Team durchgeführt wird. Da nur 2 Tage zur Verfügung standen, wurde das Format „kreativ“ komprimiert. Ziel der 2 Tage war das Kennenlernen der Methode und die Erstellung von interaktiven Prototypen für die Kundenapp unseres Partners. Bei diesem Non-Coding-Projekt stand also nicht die Programmierung, sondern die Methodik zur Generierung neuer Produktideen im Fokus. 

Design Sprint

„The Design Sprint is a five-day process for solving problems and testing new ideas.“

Invented at Google by Jake Knapp, perfected with more than 150 startups at GV, then shared with the world in the bestselling book “Sprint – How To Solve Big Problems and Test New Ideas in Just Five Days”.

“On Monday, you make a map of the problem. On Tuesday, each individual sketches solutions. On Wednesday, you decide which sketches are strongest. On Thursday, you build a realistic prototype. And on Friday, you test that prototype with five target customers.”

https://www.thesprintbook.com/
https://www.youtube.com/watch?v=K2vSQPh6MCE

In den 2 ziemlich durchgeplanten Tagen hat das Team im ersten Schritt in Experteninterviews die Challenges zum Thema Personalisierung zusammengetragen und priorisiert. Die Interviews wurden mit 2 Personen aus den Bereichen Business, Markting/Sales und Tech durchgeführt. 

How we might …
„Wie könnten wir persönlich Kunden ansprechen, ohne das es creepy wird“?

Es wurden ambitionierte 2 Jahresziele und mögliche Hindernisse auf dem Weg identifiziert. Auf dieser Basis wurden Lösungskontext und Fokusgebiet geschärft und das Team hat eine Lightning Demo Session durchgeführt. Anschließend hat jeder Teilnehmer eine eigene Produktidee ausgearbeitet und als Paper-Prototype in 3 Schritten visualisiert. Zwei Ideen wurden nach intensiven Diskussionen ausgewählt, um Flows auszuarbeiten und final interaktive Prototypen zu erstellen. 

Mit den Ideen „Find your Style” und “Love me Tinder” ging das Team in die Abschlusspräsentation. Der Hackathon-Designsprint war damit abgeschlossen. Die Phase der Nutzer-Validierung möchte das UX-Team im Nachgang zusammen mit der Product-Ownerin angehen.

Die 8 Teilnehmer waren von der Methode und den Ergebnissen begeistert. Unser UX/UI-Lead Nicolai hat den Design-Sprint sehr gut vorbereitet und moderiert. Das Team bestehend aus Designern, Product Owner, Entwickler, QA, Scrum Master und Team-Manager hat viele Perspektiven in die Konzeptphase eingebracht und so für unseren Kunden einen wertvollen Input für die Produktentwicklung geben können. 

Team KRAS – Konferenz Raum Anzeige System

Mit der Projektidee sollte ein ganz praktisches Problem in unserem Office gelöst werden. Steht man vor einem unserer Meetingräume und möchte wissen ob oder ab wann dieser belegt ist, muss man erst eine Buchungsanfrage am Rechner durchführen, um an die Information zu gelangen. Da gehen schon mal ein paar Minuten ins Land. Das geht besser!

Die Informationen sollen mit einem kleinem Display an jedem Raum angezeigt werden. Darauf soll die aktuelle Tagesbelegung dargestellt werden.

Das Team hat sich für Angular als Frontendtechnologie entschieden. Die UI ist responsiv und kann auch auf kleinen eInk-Devices dargestellt werden. Neben der Darstellung der Rauminformationen hat das Team auch damit begonnen, eine Schnellbuchungsfunktion für den Raum zu implementieren. Die Web-UI wurde mit einer Android-App auf einem modifizierten Tolino eInk-Reader und auf einem Android-Tablet dargestellt. Aufgrund der deutlich langsameren Geschwindigkeit der eInk-Displays müsste die UI im nächsten Schritt weiter optimiert werden. Z. B. ist eine Alternative für das Scrolling durch Listen nötig.

Den Code hat das Team auf Github bereitgestellt:

Conference room assisting system
https://github.com/jenszech/cras

ePaper interface for Conference Room Assistent System (CRAS)
https://github.com/jenszech/crasBadgeIt

Neben der Web-UI hat das Projektteam auch eine Arduino-basierte ePaper-Hardwarelösung mit Mikrokontroller evaluiert. Die Lösung ist äußert energiesparsam.

Die REST-API wurden über einen Node.js-Server realisiert, der auf einem Raspberry Pi läuft. Eine Herausforderung lag in der Anbindung des Thalia Exchange-Servers, der die Informationen zu den Räumen bereitstellt. Zusätzlich wurden Metadaten zu den Räumen wie z. B. vorhandene Meeting-Technik und Größe hinterlegt.

In den 2 Tagen konnte sich das Team gut in das umfangreiche Thema einarbeiten. Ein weiteres Learning war, dass für die Koordination der Subteams entsprechend Zeit eingeplant werden muss. Alle im Team waren hoch motiviert für ihre Bereiche Lösungen zu erarbeiten und diese mit den anderen zu integrieren. Die erarbeitete Lösung ist nahezu fertig, um sie mit einem ersten Meetingraum zu testen.

Team Flutter Touch – We want to Flutter you!

Mit dem Thema Cross-Plattform Entwicklung unter Anwendung von Google’s Flutter hat sich auch dieses Jahr ein Team beschäftigt. Flutter ist mittlerweile in Version 1.7 veröffentlich. Neben iOS und Android existiert auch ein Technology Preview für das Web. Seit Ende 2018 ist Flutter production-ready und die Flutter Community wächst kontinuierlich. Das Team hatte 2 Teilnehmer die bereits Projekterfahrung mit Flutter hatten. 

Unter dem Topic „Feed-Configurator” hat das Team für die Kundenapp unseres Partners eine Individualisierungsfunktion geschaffen, um die Produktbereiche wie „Für dich ausgewählt“, „Kategorien“ oder „Marken“ in der Reihenfolge zu ändern. Mit dem „Tinder-Swipe für Produkte“ Feature kann der Kunde im Tinder-Stil Produkte durch Wischen nach links oder rechts als interessant bzw. uninteressant markieren. Die Daten könnten später in eine Recommendation-Engine überführt werden, um bessere Produktvorschläge zu machen. Präsentiert wurden die Funktionen im Web, auf iOS und auf Android.

Das Tinder-Feature hat das Team sogar auf einem Android-Wearable (Uhr) im Emulator gezeigt. Großartig!

Wearables – Wie können wir Wearables im Douglas Kontext einsetzen?

Das Team hat sich einen Usecase im Kontext Personalisierung überlegt. In diesem werden Produktempfehlungen als personalisierte Push-Notification auf dem Wearable dargestellt und sollen über Smart-Actions auf den Merkzettel oder in den Warenkorb gelegt werden können. In der Filiale könnte man sich auch vorstellen die Produkte direkt über einen Paymentdienst wie Google Pay zu bezahlen.

Entwickelt wurden die Prototypen auf iOS, Android und Tizen. Über ein Google Firebase-Setup sollten die Push Notifications auf die Geräte gesendet werden. Die Anwendungen liefen jeweils in den Emulatoren der Plattformen. Die Produktdetails wurden von den Partner-APIs abgerufen und auf den kleinen Displays dargestellt. Die Entwickler waren davon überrascht, wie schnell sie auf den Wearables entwickeln können.

Unser Fazit

Das Feedback der Teilnehmer und unseres Partners Douglas war äußerst positiv. Den Hackathon auf 2 Tage zu erweitern, gab den Teams mehr Zeit sich inhaltlich intensiver mit den Themen zu beschäftigten und ihre Präsentationen vorzubereiten. Die Bandbreite der Themen war technologisch und inhaltlich groß und brachte den Teilnehmern viele interessante Einblick in die Themen, mit denen wir uns vielleicht schon morgen beschäftigen werden.

Da auch Douglas-Mitarbeiter in Berlin dabei waren, konnte wir uns persönlich besser kennenlernen und austauschen. Die Projekte waren allesamt spannendend und wir haben unser Ziel erreicht mit dem Schwerpunkt Personalisierung einen direkten Mehrwert in Form von Protoytpen, Methodik und Inspiration für Produktideen zu schaffen.

Die Abschlussphase mit den Präsentationen und die Preisverleihung hat den Hackathon-Event gekonnt abgeschlossen. Wir freuen uns schon auf das nächstes Jahr!

Und noch ein paar weitere Inspirationen …

Möchtest du mehr über uns erfahren? Dann empfehle ich dir auch die folgenden Beiträge und Links:

B2B-Entwicklung in Berlin
Hackathon 2018
Hackathon 2017
Continous Integration

Douglas KundenApp Android (Playstore)
Douglas KundenApp iOS (App Store)




hackathon@thalia in Münster 2019

Neue Technologie, die Dinge spielerisch einfach macht! Neuer Inhalt, der zählt! Neues Modell, das uns und unsere Prozesse ein bisschen besser macht. Neue Idee, die du entdecken möchtest! Welche Themen fallen dir ein? Dieser Hackathon kennt keine Grenzen.

Für den diesjährigen Hackathon haben wir uns bewusst für ein offenes Format entschieden und damit getreu dem agilen Prinzip ‚inspect and adapt‘ das Feedback zur Themeneinschränkung beim letzten Mal berücksichtigt.

Die Beteiligung sprach für sich: 18 motivierte Menschen, 5 interessante Pitch-Vorschläge und 1 Tag Zeit.

Pitch der Ideen
Der Preis!

Das Ergebnis: 3 aus 5. Die Gruppen hatten sich schnell formiert und ihren Weg jeweils nach Bullerbü, Springfield oder Entenhausen in der Meeting-Area in Speicher 6 gefunden.

Die Türen in Winterfell waren zu jeder Zeit für den uneingeschränkten Zugriff auf die eiskalten Getränke weit geöffnet. Ebenso der Blick auf den Siegerpreis, der in den Katakomben unterhalb der Burg … genug davon, ich schweife ab.


Kommen wir zu den
Themen:

Projekt: OKD – Openshift Kubernetes Distribution

Worum ging es?

Docker – auch bei uns ein Thema. Wie können wir die Administration
und Orchestrierung unserer wachsenden  Container-Infrastruktur besser und effizienter
managen? Dieser Fragestellung will das Team mit 8 Leuten auf den Grund gehen
und schaut sich das OpenSourceprodukt OKD genauer an.

OKD ist ein Upstream-Projekt für Openshift aus der Produktpalette von Red Hat und setzt auf Kubernetes auf.

Ergebnisse

oc cluster up – mit diesem Kommando ist das Team in die
Evaluierung gestartet. Das hat schon mal gut und vor allem schnell funktioniert.
Ein SingleNode Cluster wird mittels Minishift und VirtualBox aufgebaut. In
einem Gitlab-Projekt wurde eine Testapplikation mit Webfrontend und ein Dockerfile
angelegt und deployed. Parallel haben Teammitglieder versucht, Minishift auf
einem MacOs-Gerät zum Laufen zu bekommen. Nach anfänglichen Konflikten mit der
installierten VirtualBox gelang auch dies.

Während der Abschlusspräsentation konnte eine schlanke Webanwendung (Login-UI) gezeigt, eine neue Version gebaut und über eine eigene Jenkins-Pipeline deployed werden. Et voilà – es funktioniert.

Während man mit Kubernetes alles bauen kann, was man so haben möchte (Load Balancer, Network Policies, Benutzerverwaltung, …), bringt OKD alle diese Features „out of the box“ mit. OKD ist somit eine gute open source Alternative zu OpenShift.

https://developers.google.com/awareness

Projekt: Google Awareness API

Worum ging es?

Unser eCommerce-Standort in Münster befindet sich in der Speicherstadt, wir arbeiten und treffen uns in unterschiedlichen Gebäuden. Es gibt zur Mittagszeit eine Kantine auf dem Campus, die ‚Hofrunde‘ ist ein allseits anerkanntes Kommunikationsinstrument und der Parkplatz ist ebenfalls auf dem Gelände. Das Team wollte analysieren, ob die Google Awareness API für die Erfassung von Zeiten genutzt werden kann, um somit anhand von Standorten zwischen Freizeit und Arbeit zu unterscheiden. Im ersten Schritt sollte dies auf Basis der geographischen Positionen von 2 Speichergebäuden erfolgen.

Ergebnisse

Die Zeit war eng bemessen. Ein Teil des Teams hat die App installiert und ein anderes die REST-Schnittstellen zum Sammeln und Anzeigen der Tracking-Daten entwickelt.  Erkenntnis nach 6 Stunden: Die Anwendung wirkte instabil. Der Energieverbrauch durch fortlaufendes, aktives Ansprechen der App war hoch, d.h. durch fehlende Automatisierung wurden die genutzten Geräte stärker beansprucht. Eventuell war auch der gedachte Anwendungsfall hier einfach nicht der richtige. An dieser Stelle unkritisch – bei Thalia gibt es Vertrauensarbeitszeit:-).

Einige Java-Entwickler waren Teil des Teams und konnten so die genutzte Programmiersprache Kotlin kennenlernen und im Zuge der REST-Schnittstellen sofort damit entwickeln. Viele Vorteile wurden im direkten Vergleich mit Java gesehen – insbesondere die Null-Safety-Fähigkeit und ‚Lines of Code-Reduktion‘ konnte begeistern.

Das Team hat bei der abschließenden Präsentation alles gegeben und im Rahmen einer Live-Performance Tracking-Daten gesammelt. Sehr sportlich!

Projekt: Testcontainers

Worum ging es?

Mal wieder die Dev-Umgebung kaputt oder der Test schlägt erst auf der Integrationsumgebung nach dem Deployment fehl? Dieser Zustand kostet extrem viel Zeit. In unserer Microservice-Architektur haben unsere Produktteams zunehmend die Herausforderung, Test in Abhängigkeit zu SCS-Services anderer Teams auszuführen.          

In unserem Thalia-Club-Programm schließt der Kunde die Mitgliedschaft im Rahmen eines Checkout-Prozesses ab. In diesem Prozess wird eine Payback-Kontenverknüpfung durchgeführt. Dieser Prozessbestandteil liegt in der Verantwortung eines separaten Produktteams. Wie kann nun das ‚Club-Checkout-Team‘ seine End2End-Tests (Front- und/oder Backend) mit möglichst hoher Unabhängigkeit zum ‚Payback-Team‘ gestalten? Beide Services wurden auf der Docker-Infrastruktur aufgesetzt.  

Testcontainers ist ein Framework, um Docker-Container innerhalb von JUnit-Tests zu verwenden (Infrastructure-as-Code).  Das Team möchte prüfen, ob hierdurch die Tests auf der Dev- oder Integrations-Umgebung reduziert werden und die Ausführung von Tests ohne Deployment auf die Teststages möglich ist.

Ergebnisse

Darstellung der beteiligten Services

Der PoC zeigt, wie von einander abhängige Services aus unserer Produktentwicklung im Test auf der lokalen Umgebung zu starten und auszuführen sind, dass wir vor dem Deployment auf den jeweiligen Umgebungen Fehler feststellen können.  Und eben nicht hinterher;-).

Wichtig ist hierbei der Blick auf die transitiven Abhängigkeiten. Je mehr Kaskaden an inkludierten Testcontainern es gibt, desto komplizierter kann es werden. In einem Folgeschritt sollten noch die Laufzeiten angeschaut werden, die sich aufgrund der Start/Stop-Prozesse der Container erhöht haben. Das Team sieht in der Nutzung von Testcontainers eine Menge Potential. Klasse, dass sich hier Leute aus unterschiedlichen Produktentwicklungen zusammengefunden haben. So konnte schon der erste ‚Reality-Check‘ erfolgen.

Andreas, Christian, Benjamin und Julian haben die Zuschauer-Jury überzeugt.

Die Ergebnisse und die Präsentation haben dann auch am Schluss des Hackathons die Zuschauer-Jury überzeugt. Nach einer Stichwahl stand das Team als Gewinner fest. Herzlichen Glückwunsch!


Fazit

Ist es Zufall, dass sich 2 von 3 Themen rund um die Docker-Infrastruktur konzentrieren? Wahrscheinlich nicht – wir beschäftigen uns im Moment sehr mit dem Thema und wollen eine steile Lernkurve über Einsatz, Handling und Nutzen dieser Infrastruktur erreichen. Die Erfahrungen aus dem Hackathon werden uns hierbei unterstützen. Well done!

Im letzten Jahr haben wir die App-Entwicklung von Thalia von Berlin nach Münster umgezogen. Jetzt sitzen die Kollegen/innen im Büro nebenan. Macht richtig Spaß, diese teamübergreifende Zusammenarbeit zwischen App-Experten und Java-Spezialisten. Kotlin wird aktuell bei uns in der App-Entwicklung genutzt. Mal sehen, was jetzt in den ‚Java-Teams‘ passiert:-).

Mit folgenden Stimmungsbildern verabschieden wir uns und schließen mit den Worten:

Das machen wir wieder – see you in 05/2020.




Musencast VI: Interview mit Dagmar Dörner

In unserer aktuellen Folge treffen wir Dagmar Dörner. Sie ist verantwortlich für die Personal- und Kulturentwicklung bei Thalia und erzählt über ihren Eindruck von der Transition und dem damit verbundenen Wechsel in der Kultur.

Außerdem teilt sie ihre Eindrücke über einen Blog.

Viel Spaß beim Hören!

Auch auf Spotify




True Grid oder wie wir unsere Testausführung durch Parallelisierung 6x schneller machten

Bei Thalia gibt es nicht nur crossfunktionale Teams mit QAlern, es gibt auch die zentrale QA. Wir testen prozesskettenübergreifend – d.h. während die Kollegin aus Team Kaufen alle Eingabemöglichkeiten der Zahlarten durchtestet oder der Kollege aus Team Kunde alle möglichen (und unmöglichen) Lieferadressen hinterlegt, legen wir in einem Test einen Neukunden an, ändern die Zahlart, hinterlegen eine Lieferadresse, suchen einen Artikel und kaufen ihn dann. Alles natürlich automatisiert, und für 4 verschiedene Mandanten.

Insgesamt haben wir rund 100 Tests, die wir auch noch in 2 Browsern mit unterschiedlichen Auflösungen ausführen. Bis vor wenigen Wochen noch dauerte der größte Integrationstest ca. 6 Stunden… Wenn dann aber ein Hotfix aufgespielt werden soll und jemand schnell die Antwort haben möchte, ob denn die automatischen GUI-Tests fehlerfrei durchgelaufen sind, ist „Frag morgen nochmal nach“ keine angenehme Antwort – unsere Tests mussten schneller werden.

Wir überlegten kurz, ob weniger Tests in weniger Browsern oder ohne Screenshots eine Alternative wären, stellten aber schnell fest, dass unser Problem leichter mit mehr Rechnern und paralleler Testausführung zu lösen war. Selenium Grid mit allen Möglichkeiten zur einfachen Parallelisierung von ferngesteuerten Browsern war die Antwort.

Alle machen Docker, warum nicht wir? Bisher hatten wir unsere Tests auf Windows durchgeführt (die Mehrzahl der Kunden nutzt Windows), aber wenn man z.B. Zalenium (einen Docker-basierten, dynamischen Selenium Grid) nutzen will, muss man Linux als Plattform wählen. Leider scheint es mit Firefox unter Linux bei automatisierten Tests manchmal Probleme mit unserem Hauptmenü zu geben, was sich an unerwarteten Stellen über die wichtigen Elemente legt und damit den Testweitergang behindert. Außerdem kann man mit Zalenium Chrome nicht in der kleinsten Auflösung unserer Webseite benutzen – das ist uns aber sehr wichtig, daher nahmen wir wieder Abstand von einer Docker- und Linux-basierten Lösung.

Die Anzahl unserer Tests ändert sich nicht so häufig, und wie viele Nodes wir parallel brauchen, können wir einfach pro Testinstanz im Jenkins festlegen – daher entschlossen wir uns, mit einer festen Anzahl Windows-Rechner zu arbeiten, die sich jeweils mit 4 Node-Prozessen an einem Selenium-Grid (der ausnahmsweise unter Linux läuft) anmelden. Um die genaue Anzahl von verfügbaren Browser-Handles und damit die Anzahl von Threads, die pro Test verwendet werden können, zu verwalten, sei hier noch löblich auf das Lockable Resource-Plugin im Jenkins verwiesen, mit dem wir recht einfach Flaschenhälse bei der gleichzeitigen Ausführung des Tests verhindern konnten.

Jetzt haben wir also 4 Windows-Rechner als Selenium Nodes. Ein weiterer Windows-Rechner wird für stündliche Tests gegen die Produktion verwendet. Und auf denen soll man jetzt Firefox- bzw. Chrome-Versionen im Blick behalten und die passenden Treiber für Selenium (geckodriver oder chromedriver) updaten? Sowas macht man heutzutage nicht mehr per Hand, es musste eine automatisierte Update-Lösung her. Dank Git und einem Batch-Update-Skript, was nächtlich läuft, kann ich auf meinem lokalen Rechner das Zusammenspiel von Browser-Treiber und Browser testen, einchecken und am nächsten Tag davon ausgehen, dass die Updates auf alle Rechner verteilt sind.

Und wie lange brauchen unsere Tests jetzt? Wir sind tatsächlich mit Tests für alle 4 Mandanten und noch einigen wenigen komplexen Testszenarien jetzt in rund 1 Stunde mit allen Tests durch – wenn alles gut läuft und die Testumgebung nicht zu sehr verändert wurde.




Frontend-Integration mit JavaScript-Events und Self-Contained Systems

Im Zuge der Weiterentwicklung unserer Omnichannel-Plattform und der Migration des Thalia Webshop-Monolithen sind bereits zahlreiche Self-Contained Systems und Microservices entstanden. Immer wieder stellt sich dabei die Frage nach der „richtigen“ Integrationsform zwischen einzelnen Systemen. Die Lösungsmöglichkeiten sind vielfältig: HTTP/REST, Messaging und Datenreplikation im Backend oder HTML-Transklusion und JavaScript im Frontend. Eine allgemeingültige Lösung gibt es hier nicht. Je nach Anforderungen und Anwendungskontext sind unterschiedliche Ansätze möglich und sinnvoll. Nach dem Self-Contained Systems-Architekturstil wird eine Integration auf Frontend-Ebene bevorzugt, um die Kopplung zwischen den Systemen möglichst lose zu halten. Aus diesem Grund setzen wir bei Thalia bei der Integration einiger unserer Systeme auf die Frontend-Integration.

In diesem Beitrag geht es um eine spezielle Form der Frontend-Integration unter Verwendung von Client-Side-Includes und JavaScript-Events, die wir bei Thalia umgesetzt haben. Anhand eines fiktiven Beispiels soll die Umsetzung konkret demonstriert werden. Das lauffähige Beispiel findet sich auf Github.

Fallbeispiel und Problemstellung

Als fachliches Beispiel soll ein fiktiver Kaufprozess in einem Online-Shop dienen. Der Kaufprozess wird vereinfacht realisiert und besteht lediglich aus der Auswahl einer Zahlungsart und dem Kaufabschluss. Die beiden Prozesse „Zahlungsartenauswahl“ und „Kauf“ werden von zwei verschiedenen Self-Contained Systems getrennt voneinander realisiert: dem Zahlungsartenservice und Checkoutservice. Beide Systeme sind Webanwendungen auf Basis von Spring-boot mit einer eigenen Datenhaltung, Businesslogik und Präsentationslogik inklusive eigenem Frontend.

Es besteht die Anforderung, dass der gesamte Kaufprozess (Auswahl der Zahlungsart und Kaufabschluss) auf einer einzigen Seite durchlaufen werden kann, ohne diese zu verlassen. Außerdem soll die Auswahl der Zahlungsart dem „Kauf“-Prozess bekannt gemacht werden, da diese für die anschließende Auftragsverarbeitung benötigt wird. Es ist also eine Integration der Systeme notwendig, die in diesem Beispiel ausschließlich über das Frontend erfolgen soll.

HTML-Transklusion

Damit die Auswahl der Zahlungsart und der Kaufabschluss auf einer Seite möglich ist, ist es notwendig die entsprechenden HTML-Fragmente aus den beiden Systemen zusammenzuführen. In der Literatur wird dieser Ansatz auch als HTML-Transklusion bezeichnet. HTML-Transklusion beschreibt den Vorgang HTML-Fragmente zur Laufzeit nachzuladen und in einer bestehenden HTML-Seite einzubetten. Die Fragmente können als Ressourcen von unterschiedlichen Quellen bereitgestellt werden. Eingebettet bilden die einzelnen Fragmente zusammen mit der einbettenden HTML-Seite eine neue Seite. Die Granularität der Fragmente kann dabei variieren.

Die Transklusion lässt sich auf zwei verschiedene Arten realisieren: serverseitig oder clientseitig. Bei der serverseitigen Transklusion werden die referenzierten HTML-Fragmente von einem Proxy-Server vor der Auslieferung der HTML-Seite an den Client nachgeladen und eingebettet. Im Gegensatz dazu werden die Fragmente bei der clientseitigen Variante erst im Browser des Clients geladen und verarbeitet.

In dem Fallbeispiel wird das HTML-Snippet zur Auswahl der Zahlungsart in die HTML-Seite des Kaufprozesses clientseitig nachgeladen und eingebettet. Folgender Quellcodeausschnitt zeigt dafür die Implementierung der einbettenden Seite, in diesem Fall dem Checkoutservice:

function includeZahlungsartenauswahl(){
    fetch('http://localhost:8081/zahlungsarten')
        .then(function(response) {
             return response.text();
        })
       .then(function(html){
           $("#zahlungsarten-checkout-content" ).html(html);
       });
}

Mithilfe der fetch-API wird die Ressource „zahlungsarten“ aus dem Zahlungsartenservice (hier erreichbar unter localhost:8081) geladen. Der Body aus der Antwort wird mithilfe von jquery in das HTML im Checkoutservice in das Element mit der Id „zahlungsarten-checkout-content“ eingebettet. In der Antwort enthalten ist sowohl HTML als auch JavaScript. Entscheidend ist hierbei, dass beim Einbetten der Antwort auch das mitgelieferte JavaScript als solches interpretiert und ausgeführt wird. Dies wird durch die html-Funktion von jquery automatisch gewährleistet.

Durch die HTML-Transklusion befinden sich beide HTML-Teile auf einer Seite und das aus dem Zahlungsartenservice nachgeladene JavaScript hat Zugriff auf das gleiche Document Object Model wie das JavaScript aus dem Checkoutservice. Die Voraussetzungen für den Datenaustausch über JavaScript-Events sind somit geschaffen. Die folgende Abbildung zeigt das Frontend des Checkouts nach erfolgreicher HTML-Transklusion:

Ergebnis der HTML-Transklusion im Frontend

Datenaustausch über Custom-JavaScript-Events

In unserem Fallbeispiel besteht die Anforderung, dass die Auswahl der Zahlungsart an den Kaufprozess übermittelt werden sollen. Um diese Information von einem Self-Contained System zum Anderen zu überreichen werden sogenannte CustomEvents verwendet. Mit diesem speziellen Event-Typ ist es möglich, benutzerdefinierte Daten zum Objekt hinzuzufügen und zusammen mit dem Event zu transportieren. Die CustomEvent-API definiert dazu einen Konstruktor, der einen Event-Namen und einen Hash mit dem Key „detail“ als Parameter erwartet. Der folgende Quellcode zeigt die Implementierung eines CustomEvents:

let event = new CustomEvent('zahlungsart-ausgewaehlt', {detail: zahlungsartEventData.toJSON()}); 
// event auf dem Window-Objekt veröffentlichen
window.dispatchEvent(event);

Zunächst erfolgt die Instanziierung des CustomEvents mit dem namen „zahlungsart-ausgewaehlt“ und den Zahlungsinformationen im JSON-Format. Anschließend wird das Event über das Window-Objekt veröffentlicht. Ausgelöst wird die Event-Verarbeitung in unserem Beispiel durch die Selektion einer Zahlungsart.

Damit das Event konsumiert werden kann ist es lediglich notwendig einen EventListener für das definierte Event an dem Window-Objekt zu registrieren. Hierbei ist es wichtig, dass der EventListener vor der Veröffentlichung von Events registriert wird. Die Implementierung im Checkoutservice-Frontend sieht dazu folgendermaßen aus:

window.addEventListener('zahlungsart-ausgewaehlt', function (event) {
    //Event verarbeiten
    checkoutData.setZahlungsart(event.detail.zahlungsart);
}, false);

Über das Event-Objekt und den Key „detail“ hat die Funktion aus dem Checkoutservice-JavaScript Zugriff auf die transportierten Informationen. Diese Daten können nun von dem Checkoutservice für die weitere Auftragsverarbeitung genutzt werden.

Fazit und Ausblick

Durch den Einsatz von HTML-Transklusion und Custom-JavaScript-Events ist es möglich Daten zwischen mehreren Self-Contained Systems über das Frontend auszutauschen. Die Integrationsform ähnelt dem aus Messaging-Systemen bekannten Publish-Subscribe-Pattern. Das „zahlungsart-ausgewaehlt“-Event wird nach dem „fire-and-forget“-Prinzip veröffentlicht, sodass der Sender den Empfänger nicht kennen muss. Die Kopplung zwischen den Self-Contained Systems reduziert sich daher auf den Event-Namen und den auszutauschenden Daten. Beides muss beiden Systemen bekannt sein und wie eine API behandelt werden.

Beim Austausch der Daten über JavaScript-Events im Frontend ist zu beachten, dass diese Daten dem Browser im Klartext zur Verfügung stehen. Es gilt daher genau zu prüfen, ob die Integrität und Geheimhaltung der ausgetauschten Daten notwendig ist. In unserem Beispiel bestimmt der Zahlungsartenservice welche Zahlungsarten der Kunde zur Auswahl erhält. Diese Auswahl könnte beim Transport zum Checkoutservice im Frontend manipuliert werden, sodass eine zusätzliche Validierung im Backend notwendig wäre. Dies wird deutlicher, wenn die Anforderung besteht mittels Lastschriftverfahren zahlen zu können. Die an den Checkoutservice übertragenen Bankverbindungsdaten müssten ebenfalls erneut validiert werden.

Um doppelte Validierungslogik zu vermeiden besteht die Möglichkeit, entsprechende Daten zu signieren und als Hash zu übertragen. Der Checkoutservice könnte somit feststellen, ob die transportierten Daten manipuliert wurden. Dadurch wäre die Integrität der transportierten Daten sichergestellt. Dieser Ansatz fand bei der Realisierung des Thalia-Club-Anmeldeprozesses Anwendung, damit die für den Kaufabschluss benötigten Daten zwischen zwei Self-Contained Systems über das Frontend ausgetauscht werden konnten.




Continous Integration in der App-Entwicklung

Am Standort Berlin entwickeln wir für unseren B2B-Partner Douglas unter anderem die Kunden-App [2], [3]. Einhergehend mit dem Ausbau der App-Entwicklungsaktivitäten haben wir in den letzten Monaten den CI-Ansatz überarbeitet. Ein zusätzliches Team sollte am selben Produkt – der KundenApp – mitarbeiten und die App sollte öfter veröffentlicht werden. Mit Methoden und Tools aus dem Bereich Continous Integration wollten wir dafür sorgen weiter zuverlässig und mit hoher Qualität zu liefern. Und das natürlich automatisiert. Neben der Technik geht es auch um die Teams. Wie sind sie vorgegangen und welche Hürden haben sie genommen.

Komplexität und Feedback

Je mehr Personen gleichzeitig an einem Produkt entwickeln, desto größer wird die Wahrscheinlichkeit, dass unbeabsichtigte Seiteneffekte auftreten. Gleichzeitig steigt der Umfang der App, da wir konstant neue Funktionen hinzufügen und bestehende Funktion ändern. Um die Komplexität weiter zu beherrschen, ist schnelles Feedback zu Änderungen ein entscheidender Faktor, um Probleme schnell zu korrigieren. Wie wäre es, automatisiert ein Feedback nach jedem Commit zu bekommen und darauf nur kurze Zeit warten zu müssen? Genau hier setzten wir an.

Schnelles Feedback erhalten wir durch den Einsatz von Feature-Toggles und durch die Ausführung von automatischen Tests im CI-Prozess. 

Feature Toogles 

Feature Toogles ermöglichen es uns Codeänderungen aller Entwickler kontinuierlich in einen gemeinsamen Integration-Branch zusammenzuführen. Und das auch, wenn Features noch nicht fertig bzw. für den Kunden nicht sichtbar sein sollen. In der Vergangenheit haben wir solche Features für mehrere Tage, manchmal Wochen, in separaten Branches entwickelt und erst am Ende der Entwicklung in den Integration-Branch gemergt. Das Feedback kam entsprechend spät. Traten Probleme auf, war es durch die Vielzahl der Änderungen mitunter schwer festzustellen, welche konkrete Änderung zum Bruch geführt hat. Die Integration hatte das Potential unsere Zeitplanung empfindlich zu stören. Diese Bing-Bang-Szenarien sollen durch Toggles und kontinuierliche Integration abgefedert werden. 

Toggles und Diskussionen

Der Einsatz der Feature-Toggles wurde im Team intensiv diskutiert, denn die Einführung erhöht erstmal die Komplexität – und liebgewonnen Pfade, wie die isolierte Arbeit im Feature-Branch, standen auf einmal auf dem Prüfstand. Es gab diverse Pros und Cons. Auch musste ein gemeinsames Verständnis beim Thema Toggle-Mechanik erarbeitet werden. In Bezug auf Dynamik und Langlebigkeit der Toggles gab es unterschiedliche Auffassungen, da viele schon mal irgendetwas mit Toggels gemacht hatten. 

Wir haben uns am Ende auf die Nutzung von Feature-Toggels zur Entwicklungszeit – auch Release-Toggles genannt – geeinigt. Sie werden für den Zeitraum weniger Tage/Wochen genutzt. Ist das Feature fertig entwickelt, wird der Toggle aus dem Code komplett entfernt. Der Artikel auf martinfowler.com [1] sei dem interessierten Leser an der Stelle empfohlen. 

https://martinfowler.com/articles/feature-toggles.html

2 bis 3 Feature-Toggle sind im Durchschnitt parallel im Einsatz. In unserem Jenkins haben wir durch einen manuellen Schritt in der Build-Pipeline die Möglichkeit geschaffen, einen einzelnen Toggle zu aktivieren und somit App-Artefakte für Features (apk, ipa) für das Testing zu bauen. Ist das Feature komplett entwickelt, wird der Toggle aus dem Code entfernt. Mit dem nächsten App-Release ist das Feature dann für den Kunden sichtbar.

Was ist ein Feature Toggle?

Ein Feature Toggle ist eine Progammiertechnik, die es erlaubt ein Feature oder eine Funktion vor Kunde ein- bzw. auszuschalten. Also die Sichtbarkeit zu ändern. Entwicklungsteams aktivieren Features beispielsweise um noch nicht fertige Funktionen integrieren und testen können. Ist ein Feature fertig, kann es ohne großen zusätzlichen Merge-Aufwand veröffentlicht werden, da die Arbeit in separaten Branches entfällt. Feature Toggles können auch dafür genutzt werden, die Sichtbarkeit von Funktionen zur Laufzeit der Anwendung zu ändern. Z.B. im Rahmen von A/B Tests oder wenn die Sichtbarkeit zu einer bestimmten Zeit geändert werden soll.

Toogles im Code

iOS

Unter iOS wird ein Feature in der App über eine Environment-Variable in der Launch-Konfiguration aktiviert (z.B.: USE_NATIVE_PRODUCT_LIST = 1). Im Code wird dann an relevanten Stellen über eine Abfrage entschieden, ob bestimmte Codestellen zur Ausführung kommen oder nicht.

if toggleRouter.isNativeProductListEnabled() {
  // Feature Code
}

Android

Es gibt ein Interface, in dem alle Toggles als Methoden definiert werden. Diese Methoden werden mit Java-Annotations annotiert und geben immer ein Boolean zurück – TRUE für Feature aktiv, FALSE für nicht aktiv.

@ReleaseToggle(BuildConfig.FEATURE_PRODUCT_LIST)
fun isProductListEnabled(): Boolean

Eine eigens dafür entwickelte Library mit einem Annotation-Prozessor wird während der Build-Phase ausgeführt: Dieser schaut in einer Konfigurations-Datei (json) nach, ob das jeweilige Feature getoggelt werden soll. Wenn das Feature eingeschaltet werden soll, muss der String, der sich in der Annotation befindet, hier eingetragen werden.

[
    "FEATURE_PRODUCT_LIST"
]

Der Prozessor baut dann jeweils die Implementation für das Interface zusammen. In diesem Fall würde die implementierte Methode TRUE zurück liefern. Wäre der String FEATURE_PRODUCT_LIST nicht in der Datei, wäre es FALSE.

So kann man auf jedem lokalen Rechner die Features beliebig ein- und ausschalten. Auf dem Jenkins kann man das genauso machen, hier editieren wir nicht manuell die Datei sondern sagen ihm über das Blue Ocean Plugin, welches Feature getoggelt werden soll.

Und die jeweiligen Code-Stellen togglen wir, in dem wir die Interface-Implementation prüfen:

if (ReleaseToggleConfiguration_Impl().isProductListEnabled()) {
     // Mach was mit der Product list
}

Ein gemeinsames Traffic Light für Build und Test

Eine weitere zentrale Komponente im CI-Prozess stellt die Testautomatisierung dar. Das Feedback, dass Build und Test erfolgreich nach einem Commit auf Integration durchgelaufen sind, wird durch eine Ampel visualisiert. Diese ist für jedem im Team sichtbar. Ist sie rot, ist das gesamte Team angehalten den Grund zu ermitteln und die Ampel wieder auf „grün zu bekommen“. Also Fehler zu korrigieren, Tests oder die Automatisierung anzupassen.

CI-Build-Status für Android und iOS

Die Tests sind eine Kombination aus Unit-Tests und End-2-End-Tests (Akzeptanztests). Die End-2-End-Tests laufen auf echten Geräten bzw. Simulatoren im Zusammenspiel mit dem Backend.

Continous Integration Process

Unser CI Prozess sieht wie folgt aus:

Nach dem Approval der Codeänderung in Gitlab und Integration in den Integrations-Branch baut der Jenkins das App-Artefakt, führt die Unittests aus und startet die End-2-End Tests. Das kombinierte Ergebnis aus Build/Unitests und End-2-End Test wird auf der Ampel dargestellt.

Für den Test eines Features, dass sich noch in der Entwicklung befindet, wird ein Feature-Toggle manuell im Jenkins aktiviert, die App gebaut und die Unittests ausgeführt. Die End-2-End Tests werden zum jetzigen Zeitpunkt noch nicht ausgeführt. Zum einen müssten die Tests für das Feature bereits angepasst und erweitert sein. Das ist noch nicht der Fall. Ein weiterer Grund sind die noch fehlenden Ressourcen in Form von Hardware und Testgeräten. Ein nächster Schritt.

Learnings zu Tools, Integrationslevel und Verantwortung

Auf drei Learnings möchte ich an der Stelle speziell eingehen. 

Der Leser soll dazu wissen, dass unsere Entwicklungsteams Cross-funktional aufgebaut sind. Ein Entwicklungs-Team besteht aus iOS-Entwicklern, Android-Entwicklern, Backend-Entwicklern und einem Quality Engineer. Die Backend-Entwickler sitzen an einem anderen Standort und sind wie wir Dienstleister für den B2B-Partner. Folgende Tests führen wir zur Zeit durch:

Testtypen

Beim Tooling ging es vor allem um die Wahl des Testautomatisierungstools. Da die QA in der Vergangenheit auf Appium gesetzt hat, wollten wir die Technik auch für unsere CI-Tests im gesamtem Team nutzen (Dev+QA). Bisher wurden die Appium Test ausschließlich von der QA geschrieben und waren nicht in einem gemeinsamen CI-Prozess zusammen mit dem Build integriert. Es stellte sich heraus das die Akzeptanz des Tools für iOS unter den Entwicklern sehr gering war. Stabilität, Funktionsumfang, Integrierbarkeit und Ausführungsgeschwindigkeit überzeugten nicht. Unsere Teams haben sich daher entschieden, für iOS die End-2-End Tests auf Basis von XCUITest neu zu schreiben. Für den Android Bereich setzen wir vorerst weiter auf Appium.

Ein weiteres Learning gab es beim Integrationslevel. Unsere End-2-End Tests weisen ein hohes Integrationslevel auf: die App wird im Zusammenspiel mit dem Backend getestet. Fehler im Backend oder eine schlechte Verbindungsqualität können dazu führen, dass Tests fehlschlagen, obwohl die App „korrekt“ funktioniert. Die Ampel zeigt rot, obwohl „mit der App alles in Ordnung ist“. Flaky Tests bzw. instabile Tests senken die Aussagekraft der Ampel deutlich und führen dazu, dass die Teams einer roten Ampel weniger Aufmerksamkeit schenken. Neben dem End-2-End Test planen wir daher einen zusätzlichen Testtyp einzuführen, der vom Integrationslevel zwischen Unittest und End-2-End Test liegt. Ziel ist es die App ohne Backend zusätzlich zu verifizieren. Dafür sollen Backend-Responses „gemockt“ und die Tests auf der UI-Ebene durchgeführt werden. Die Test sollen eine Ergänzung zu den End-2-End Tests werden.

Beim Thema Verantwortung gehen wir mit dem CI-Ansatz ebenfalls neue Wege. Das Ergebnis aus Build + Test in einem gemeinsamen Ampelstatus zu visualisieren und damit jeden im Entwicklungs-Team zu aktivieren, Probleme im Test oder Build zu analysieren, erfordert, dass sich Entwickler mehr mit dem Thema Testing und sich die QA mehr mit der Automatisierung auseinandersetzt. Dieser Veränderungsprozess benötigt Zeit und den Willen aller Beteiligten sich zu verändern. In unserem Produktteams ist diese Veränderung explizit gewünscht und alle im Team sind angehalten für den Prozess Verantwortung zu übernehmen und ihn aktiv weiterzuentwickeln.

Ausblick

Im Bereich Testing steht der Ausbau der Testautomatisierung für die End-2-End Tests und die Einführung der zusätzlichen Test-Verifikationsstufe für die Android-App mit Espresso als UI-Testing Tool an.

Um die Qualität zu steigern, möchten wir automatisiert statische Codeanalysen durchführen und Metriken wie beispielsweise die technische Schuld ermitteln.

Verweise

[1] https://martinfowler.com/articles/feature-toggles.html

[2] Douglas App iOS: https://itunes.apple.com/de/app/douglas-parfüm-kosmetik/id394685685?mt=8

[3] Douglas App Android: https://play.google.com/store/apps/details?id=com.douglas.main&hl=de