Ein Überblick
Ich bin Steffen Gruschka, arbeite seit nunmehr einem Jahr bei basecom und bin im Bereich Mobile Entwicklung als iOS-Entwickler tätig. Zu basecom führten mich neben dem guten Ruf vor allem die spannenden und vielfältigen Projekte, welche großes Potential für eine innovative Masterarbeit boten. So kam es dazu, dass ich im August 2016 mit meiner Mastarbeit „Analyse und Weiterentwicklung des Buildprozesses im App-Framework, einem Continuous-Delivery-System für iOS-Applikationen“ begann.
Um die Prozesse im basecom App-Framework optimieren zu können, ging es in meiner Masterarbeit vor allem darum, jene Prozesse zu ermitteln, die einen reibungslosen Arbeitsablauf im App-Framework behindern. Weiterführend wurde die konzeptionelle Behebung, bzw. Automatisierung dieser Prozesse sowie einer Verifizierung der erarbeiteten Konzepte mittels einer prototypischen Umsetzung behandelt. In diesem Zuge bot sich die Integration eines neuen Konzeptes für die Auslieferung von Apps im App-Framework an: Continuous Delivery. Dieses Konzept setzt auf die Automatisierung und Regelmäßigkeit von Builds und Tests, um letztendlich die Integrationsfähigkeit und Qualität einer Software sowie die Zufriedenheit der Kunden zu verbessern.
Der Titel der Arbeit suggeriert zwar, dass das App-Framework bereits ein Continuous Delivery System sei, diese Annahme stellt sich während der Einarbeitung und der tatsächlichen Analyse jedoch als nur teilweise wahr heraus, sodass ebenfalls die Konzepte von Continuous Delivery mit denen des App-Frameworks in Einklang gebracht werden mussten.
Continuous Delivery – Ein Szenario
Wer kennt es nicht, das klassische Worst Case Szenario? Der Release-Termin ist da und die App ist bereit in den Store hochgeladen zu werden. Bis zum Vorabend wurde unter Hochdruck an den letzten Funktionen gearbeitet und ein Ende ist in Sicht. Ein letzter Test… eine bereits seit Wochen implementierte Kernfunktion erfüllt wie von Geisterhand nicht mehr ihren Dienst. Ein Desaster. Wieder einen Fehler beheben und endlich die App ausliefern. Zufrieden fährt man nach Hause, um am nächsten Morgen die Crash-Meldungen im Posteingang zu sehen. Jeder der auch nur Teile dieser Geschichte kennt wird sich gesagt haben: „Nächstes Mal mache ich es besser!“. Aber wie?
Continuous Delivery ist ein relativ junges Konzept in der Software-Entwicklung, das im Kern auf Continuous Integration (kurz: CI) aufbaut, welches wesentlich bekannter ist. Kurz gesagt fordert CI den Entwickler dazu auf, seinen Code-Stand permanent in den Hauptbranch eines Repositories zu committen. Eberhardt Wolff benennt hierfür in seinem Buch „Continuous Delivery: Der pragmatische Einstieg“ einen Zeitraum von mindestens einmal pro Tag, bzw. sogar häufiger. Ein zentralisierter Build-Prozess auf einem CI-Server (z.B. Jenkins) baut das Projekt anschließend mit jeder entwickelten Integration.
Effekte, die durch dieses Vorgehen erzielt werden sollen sind zum Beispiel:
- Weniger Probleme bei der Integration eines neuen Code-Standes mittels Kontinuität
- Lauffähige Versionen dank automatischer Builds
- Fehler fallen aufgrund von mehr Mitarbeitern am gleiche Code-Stand schneller auf
Continuous Delivery (kurz: CD) erweitert dieses Vorgehen von der Integration auf die Auslieferung einer Software. Die kontinuierliche Auslieferung baut ebenfalls auf kurze Zeitabstände, in denen die neue Version einer Software mindestens auf einem weiteren Testsystem ausgeliefert wird. Jede Auslieferung wird begleitet von Tests, die automatisiert durchlaufen werden und bei einem Fehler den Prozess abbrechen. Diese Testabfolge wird auch als Continuous Delivery Pipeline (kurz: CD Pipeline) bezeichnet.
Diese Pipeline beinhaltet automatisierte Unit Tests zur Kontrolle auf Klassen- oder Funktionsebene sowie automatisierte Akzeptanztests zur Absicherung, sodass alle Anforderungen des Kunden an das Projekt erfüllt sind und ebenfalls automatisierte Leistungstests um Performance und Durchsatz zu testen. Explorative Tests sind der einzige manuelle Schritt und können auch extern (z.B. Crowd-Testing) durchgeführt werden. Dadurch, dass diese Pipeline bei jeder Auslieferung (und optional bei jeder Integration) durchgeführt wird, kann ein hohes Maß an korrekter und fehlerfreier Funktionalität gewährleistet werden. Plötzlich auftretende Fehler, wie in unserer kleinen Geschichte reduzieren sich enorm.
Weitere Vorteile:
- Automatisierte statt manueller Prozesse
- Zeitersparnis für Entwickler
- Viele kleine und nicht ein großer Release
- Permanente Releases (während der Entwicklung auch z.B. als Testversion auf Hockey)
- Weniger Probleme beim eigentlichen Release
- Kleinere/weniger Features pro Release
- Schnelle Veröffentlichung neuer Features (kurze „time-to-market“)
- Weniger Regressionsfehler
- Erhöhte Code-Qualität, erhöhte Kundenzufriedenheit
Der wichtigste und namensgebende Faktor ist die Regelmäßigkeit der Durchführung. Wolff sagt auch: „Regelmäßigkeit führt meist zur Automatisierung“ und „Regelmäßigkeit führt zu Zuverlässigkeit“.
Die Umsetzung von Continuous Delivery
Um Continuous Delivery für das App-Framework umsetzen zu können, wurde auf den Jenkins CI-Server gesetzt. Durch seinen modularen Aufbau bietet er eine passende Umgebung für Build-Prozesse nahezu aller Technologien. Durch Build Trigger können zudem Builds automatisiert mit jeder Integration eines neuen Code-Standes ausgeführt werden.
Den Build-Prozess an sich übernimmt fastlane. Dieses Toolkit bietet alle Funktionen, die für eine automatisierte Auslieferung von iOS und auch Android Apps benötigt werden. Dazu gehören auch automatisierte Builds, Tests, Screenshots und vieles mehr.
Die Umsetzung von Unit Tests nimmt den wohl größten Teil der Umsetzungskapazitäten der CD Pipeline ein. Denkbar war ein stufenweises Vorgehen, welches Unit Tests je nach Wichtigkeit der jeweiligen Klasse im Projekt erweitert. So sind zum Beispiel Datenhaltungsklassen höher zu priorisieren als kleinere UI-Klassen. Auch ein Testen auf der obersten Abstraktionsebene (Stichwort: Mehrfachvererbung) ist denkbar und erleichtert den Einstieg in die Umsetzung.
Akzeptanztests sind ein eher kompliziertes Thema und wurden in der Masterarbeit selbst nur theoretisch behandelt. Grund hierfür sind Schwierigkeiten, die das (anscheinend) einzige relevante Framework zu deren Integration in iOS macht. Wie sich diese Tests also genau einbinden lassen bleibt vorerst der Zukunft überlassen.
Leistungstests sind im App-Framework essentiell und messen die Zeiten, die ein Call an die API benötigt, um die Performance bestimmen zu können. Da im Normalfall mit jedem Seitenaufruf ein API-Call getätigt wird ist dies klar der Flaschenhals des Systems und somit dringend kontinuierlich zu testen. Weitere Leistungstests, wie etwa für den Durchsatz wären Backend-seitig selbstverständlich denkbar. Alle Tests lassen sich über fastlane mittels scan sehr einfach Einbinden und integrieren sich nahtlos in den bereits bestehenden Build-Prozess.
Neben dieser technischen Seite, die vor allem mit dem Einbinden von automatisierten Tests entsteht, gilt es verschiedene organisatorische und prozessspezifische Vorgehensweisen zu adaptieren, um CD erfolgreich umzusetzen:
- Hohes Maß an Kommunikation im Team
- Wissensaustausch auch zwischen den Teams
- Einhalten der „Kontinuität“ von Integration, Build und Release
- Disziplin von Entwicklern bei Integration von neuem Code
Sollte die Integration von CD im App-Framework fortgeführt werden, muss sich in der Zukunft zeigen, wie sich sowohl die technische, als auch die organisatorische Ebene in Form von Zeitersparnis, Software-Qualität und letzten Endes Kundenzufriedenheit und Erfolg auszahlen.
Die Prozessoptimierung
Die eigentliche Herausforderung bei der technischen Umsetzung von Continuous Delivery stellt die Vielfältigkeit an Apps dar, die das App-Framework bedienen kann. Diese lassen sich bequem im Backend konfigurieren und übernehmen beim ersten Start die ihnen zur Verfügung gestellte Konfiguration. Für jede App existiert dazu unter iOS ein Build Target (> 65 Targets insgesamt). Dieser Zustand verursacht gleich mehrere Probleme und Unannehmlichkeiten:
- Große Projektdatei
- Probleme mit Merge Requests in Git
- Langsame Indizierung von Dateien in Xcode (> 35min) -> Autovervollständigung unzureichend
- Langsame Ausführung von pod install (> 1:20 min)
- Manuelles Anlegen eines neuen Targets für jede neue App
- Händisches Kopieren von Konfigurationen
- Menschliche Fehler nicht unüblich -> Release dauert länger
Die Lösung hierfür ist trivial. Das App-Framework behält lediglich ein Target, welches durch externe Lösungen in Form eines Bash-Skriptes (für den automatischen Build-Prozess) oder eines externen Programms (für die Entwicklung) rekonfiguriert wird. Dieses löst gleichzeitig eine Vielzahl an Problemen und ermöglicht (theoretisch), neue Apps nach dem Anlegen mit nur einem Klick im Backend zu erstellen. Da die Konfigurationen (Xconfig-Dateien) nun vom Backend übernommen werden, sind auch hier Anpassungen nötig, um diese in dessen Datenbank zu verwalten und über eine API für den CI-Server oder Entwickler zur Verfügung stellen zu können.
Ein letzter Schritt, der einem vollständig automatisierten Build-Prozess auf dem CI-Server im Wege steht, ist die momentan manuelle Aufnahme von Screenshots für jede App. Hierfür bietet fastlane das snapshot-Tool zur Automatisierung, mit dem sich Screenshots einfach mittels automatischer UI-Tests in Xcode aufnehmen lassen. Diesbezüglich erfolgt im Backend eine Konfiguration der gewünschten Ansicht für jeden Screenshot einer App. Somit kann das App-Framework auch an dieser Stelle mit einer zentralen Konfiguration, für alle Apps eine zentralisierte Lösung bereitstellen und fastlane mitteilen, welche Ansichten zu Screenshots verarbeitet werden sollen.
Der Ablauf
Prozessoptimierung und technische Integration von Continuous Delivery werden im weiteren Verlauf der Masterarbeit prototypisch umgesetzt. Diese Umsetzung bestätigt die Konzeption: eine App lässt sich inklusive Screenshots ohne Weiteres automatisch builden und in den AppStore laden und zwar auf Knopfdruck. Dank der Integration von Tests ist auch die Funktionsfähigkeit gesichert. Aber wie sieht es in der echten Welt aus?
Dort gibt es im App-Framework eine Vielzahl an Kunden, deren vertragliche Konditionen es nicht erlauben, einfach alle 2 Wochen eine neue App-Version in den Store hochzuladen oder bei Hockey zu veröffentlichen. Also: Was tun?
Die Lösung liegt in der Eigenschaft des App-Frameworks, eine Code-Basis für alle Apps bereitzustellen, die sich die enthaltenen Apps teilen. Es entsteht also das Konzept einer Test App, die alle momentan relevante Module des App Frameworks enthält. Der aktuelle Entwicklungsstand mündet nun alle 2 Wochen in einer neuen lauffähigen Version der Test App, die sowohl den Build-Prozess und die CD Pipeline, als auch explorative Tests auf test.io durchläuft. Somit können alle anderen Apps indirekt mitgetestet werden. Bei jedem Release erfolgt natürlich der gleiche Prozess mit der zu veröffentlichenden App. Der eigentliche technische Prozess funktioniert also. Einzige Voraussetzung ist die noch ausstehende Anpassung des Backends. Liefert diese die korrekten Daten wird der Prozess neben der getesteten App auch alle anderen App Framework Apps automatisch ausliefern können.
Das Ergebnis
Die Prozessoptimierungen und die Integration von Continuous Delivery in das App-Framework sind als Erfolg zu werten. Auch wenn Akzeptanztests sich (noch) nicht umsetzen lassen, war ich in der Lage einen komplett automatisierten Build-Prozess zu etablieren und nebenbei weitere Prozesse in der Entwicklung für das App-Framework zu verbessern. Durch die Reduktion der Targets schaffte ich eine Verringerung der Zeiten für
- pod install von 1:20 min auf 18 Sekunden
- die Indizierung aller Quellcode-Dateien von 35 min auf ca. 50 Sekunden
Somit wurde sowohl der Komfort als auch die Effektivität erhöht, da zum Beispiel die Code-Vervollständigung besser bzw. schneller funktioniert. Zusätzlich sind alle in der Arbeit entstandenen prototypischen Umsetzungen (mit Ausnahme der Unit Tests) mit relativ geringem Aufwand realisierbar. Merkwürdig war, dass ich so gut wie keine Nachteile über Continuous Delivery in der Literatur finden konnte. Vielmehr sprachen einige Quellen von „Herausforderungen“, seien sie organisatorischer Natur (z.B. Zusammenarbeit auch zwischen den Teams), prozessspezifisch (z.B. Umstellung auf festgelegt Zyklen und deren Einhaltung) oder technisch (z.B. Zusammenspiel von Technologien, Automatisierbarkeit, …). Um CD erfolgreich umzusetzen gilt es diese zu bewältigen.
Am wertvollsten sind jedoch die Erkenntnisse, die ich für weitere App-Projekte gewinnen konnte:
- Von Projektbeginn an auf Unit Tests setzen und am besten Test Driven entwickeln
- Für jede neue App Projekt die Konzepte von Continuous Delivery realisieren:
- Jenkins Job
- Build-Prozess mit fastlane
- Automatisierte Tests
- Kontinuierlich Integrieren und Builden, sowie während der Entwicklung bereits Testversionen mit Hockey ausliefern
- Den Kunden direkt für diese Vorgehensweisen sensibilisieren und von kleineren, jedoch kontinuierlich neuen App-Versionen überzeugen
Meine Empfehlung ist daher zukünftig die Continuous Delivery-Konzepte für neue App-Projekte zu verinnerlichen und diese schon bei der Konzeption der App an den Kunden zu kommunizieren und zu verkaufen. Gerade im Bereich Apps ist es wichtig an den aktuellen Entwicklungen dran zu bleiben und diese für eigene Produkte zu übernehmen. Dazu müssen neue Features möglichst schnell und zuverlässig integriert werden. Auch dabei kann Continuous Delivery durch kurze Release-Zyklen helfen.