Die kontinuierliche Qualitätssteigerung

Wie ihr aus unseren älteren  Blogbeiträgen entnehmen könnt(hier und hier), sind wir stets bemüht, unsere Qualitätsansprüche auf das nächste Level zu hieven und unsere Qualität zu verbessern. Ein Prozess, der dabei hilft, ist CI – Continuous Integration.

Continuous Integration, im Weiteren einfach CI genannt (nicht zu verwechseln mit dem anderen CI – Corporate Identity), beschreibt grundsätzlich den Prozess des kontinuierlichen Zusammenführens verschiedener Software-Komponenten. Allerdings, wie wir im weiteren Verlauf sehen werden, kann dies noch viel mehr beinhalten.

CI allgemein

Grundsätzlich soll CI das Zusammenarbeiten erleichtern und dabei die Qualität verbessern. Bei Verwendung eines VCS (version control system) wird Software-Code in einem Repository gesichert. Jeder Entwickler, der an dem Projekt mitarbeitet, kann aus diesem Repository den Code lokal sichern und daran weiterarbeiten. Sobald eine Applikation oder Komponente fertig gestellt ist, wird der Code committet, ins Repository gepusht und mit dem vorherigen Code zusammengefügt. Das VCS fügt den neuen Code nahtlos in das bestehende System ein. Und genau hier setzt CI an. Sobald sich der Quellcode der Software verändert, kann man nicht mehr sicher sein, dass alles noch genauso funktioniert, wie vor der Änderung. Mittels CI wird, sobald gepusht wird, überprüft, ob der neue Code sich wirklich nahtlos in das schon vorhandene System einfügt. Die einfachste dieser Überprüfungen ist das automatische Starten eines Builds, also das Übersetzen des Codes in eine Hardware-verständliche Sprache – je nach Software demnach z.B. in eine App für das Smartphone oder ein Programm für Windows. Da dies bei jedem neuen Code gemacht wird, kann so schnell festgestellt werden, welcher Code-Teil den Fehler auslöst und was den Build fehlschlagen lässt.


Quelle: https://dotnetvibes.com/2018/04/10/continuous-delivery-is-not-continuous-deployment/

Das bedeutet allerdings auch, dass alle Entwickler ihren Fortschritt am besten sofort nach dem Fertigstellen pushen. Außerdem ist es auch wichtig, nicht zu viele und zu große Änderungen auf einmal zu machen, um im Fehlerfall den Bug einfach finden und ausbessern zu können.
Wie gesagt, das ist noch die unterste Ebene der CI. Aufbauend darauf, können somit sofort nach einbringen des neuen Codes auch Software-Tests durchlaufen werden, um weitere Funktionen bestätigen zu können. Dazu aber später noch mehr.

Git und Jenkins

Damit CI funktioniert, benötigt man natürlich auch die Grundlagen, um das System aufzubauen. Als VCS wird bei uns Git verwendet. Jedes Projekt beinhaltet verschiedene Zweige (Branches), welche unterschiedliche Fortschritte in der Entwicklung darstellen, z.B. die Entwicklung einer weiteren Funktion einer App. Sobald ein ausgegliederter Zweig abgeschlossen ist, wird er in den ursprünglichen wieder eingefügt.

Ein weiterer Baustein für die Grundlagen ist Jenkins. Jenkins ist ein wichtiges Open-Source CI-Tool und läuft auf unserem internen Server. Dort werden die verschiedenen Build-Jobs verwaltet, welche dafür sorgen, dass, sobald eine Änderung gepusht wird, der Build-Vorgang startet. In der Web-Ansicht von Jenkins wird für jedes neue Projekt ein Job angelegt und das Git-Repository angegeben. In jedem Branch, der per CI eingebunden werden soll, liegt ein Jenkins-File. Durch periodische Abläufe wird überprüft, ob neuer Code im Branch liegt und wenn dies der Fall ist, wird das Jenkins-File ausgeführt.

Fehlerpotential

Natürlich muss man auch bei einem CI-Prozess aufpassen, dass diverse Fehler vermieden werden. Dafür gibt es eine kleine Übersicht, die einige grundlegende Spezifikationen bestimmt:

  • – Seltene Commits
    Ein Fehler, der häufiger vorkommen kann, ist das zu seltene Committen des neuen Codes. Dadurch werden Neuerungen nicht gleich getestet und man kann Fehler die zu einem fehlgeschlagenen Build führen nicht mehr genau nachvollziehen. Daher sollte neuer oder verbesserter Code möglichst bald und regelmäßig committet werden, um so etwas zu verhindern.
  • – Fehlgeschlagene Builds
    Natürlich sind fehlgeschlagene Builds an sich kein Fehler. Schließlich dient CI dazu, dass man solche Fehler eher entdeckt und beheben kann. Daher sollte, sobald ein Build nicht mehr erfolgreich ausgeführt werden kann, gleich nach dem Fehler gesucht werden. Ziel ist, diesen nicht über mehrere Build-Vorgänge „mit zu schleppen“, nur um ihn dann nicht mehr genau nachvollziehen zu können.
  • – Zu wenig vs. zu viel Feedback
    Da an einem CI-Projekt meist mehrere Personen arbeiten, ist es wichtig zu unterscheiden, welches Feedback an wen weitergeleitet wird. Wenn bei jedem Build eine E-Mail und/oder Notifikation ausgeschickt wird, kann das schnell dazu führen, dass diese Nachrichten aufgrund des Überflusses einfach überlesen oder ignoriert werden. Daher ist es wichtig, einen passenden Feedback-Rhythmus in das System einzupflegen. Eine gute Möglichkeit ist auch eine visuelle Darstellung, die z.B. auf dem Haus-internen Server läuft, sodass jeder jederzeit die Möglichkeit hat, auch dort die Build-Verläufe zu verfolgen.
  • – Langsamer Build Server
    Natürlich sollte auch bei den Kosten für die Infrastruktur nicht gespart werden. Ein zu langsamer Build-Server benötigt mehr Zeit für das Feedback, daher dauert es länger bis die Entwickler wissen, ob ein neuer Task gestartet werden kann. Natürlich sind mit besserer Hardware auch höhere Kosten verbunden, allerdings wird beim Builden Zeit gespart und damit auch wieder Geld.

Ablauf

Um euch den Ablauf der CI ein bisschen näher zu bringen, werde ich noch ein Beispiel aus unserem Workflow zeigen und zudem gleich noch auf einige weitere Einbindungsmöglichkeiten eingehen.
Den grundsätzlichen Aufbau mit Git und Jenkins habe ich schon erklärt. Am Anfang eines Projekts wird auf dem Jenkins ein neuer Job erstellt. Sobald ein Build getriggert wird (automatisch oder manuell), startet der Server den Build-Job und führt die im Jenkins-File angegebenen Schritte aus. Diese Schritte bestehen im Weiteren aus dem Testen, dem Builden und meistens noch dem automatische Deployen (Bereitstellen) der App. Falls einer dieser Schritte fehlschlägt, wird der Build abgebrochen und es wird in einem im Jenkins-File definierten Slack-Channel bekannt gegeben, was passiert ist. Ebenso wird bei positivem Abschluss eine Erfolgsmeldung geschickt.

Sobald diverse Tests durchgelaufen sind, wird der Code von Sonarqube analysiert und die Ausgabe verlinkt. Sonarqube zeigt in einer Übersicht, welche möglichen Fehlerquellen der Code enthält und gibt außerdem z.B. die Ergebnisse von Unit-Tests und die dazugehörige Code-Coverage an. Somit wird, sobald neuer Code committet wird, dieser auch gleichzeitig auf Fehler überprüft und das Ergebnis bekannt gegeben, wodurch sich die Qualität des Codes verbessert. Wenn die Tests erfolgreich waren und es zu keinem Abbruch kam, wird im nächsten Schritt der Build gestartet und die App wird gebaut. Sobald eine fertige Version der App bereitsteht, also der Build erfolgreich war, geht’s gleich weiter zum nächsten Schritt: dem Deployen. Dabei wird die fertige App auf unseren internen Server geladen und steht ab sofort zum Testen bereit. Dadurch ist man auch nicht mehr von einem Entwickler abhängig, wenn eine aktuelle App-Version benötigt wird, da man per Link direkt auf die neu gebuildete App zugreifen kann und nicht warten muss, bis die App lokal gebaut wird.

Fazit

Wie ihr seht, kann Continuous Integration also einige Schritte im Verlauf eines Projekts wesentlich erleichtern. Einem Jenkins-Job können noch weitere Funktionen hinzugefügt werden, in dem man das Jenkins-File im jeweiligen Branch anpasst. So lässt sich schon ein sehr großer Teil des Projekts automatisieren, was wiederum zu einer großen Zeitersparnis führt und gleichzeitig durch die automatisch durchlaufenen Tests eine höhere Qualität sicherstellt. Und außerdem: Wenn einem, nach einem erfolgreichen Build, das Chuck-Norris Plugin den Daumen entgegenstreckt, ist das schon eine große Motivation, noch besseren Code zu schreiben!


Ansicht nach einem erfolgreichen Build

 

Quelle Titelbild: https://cloud.google.com/solutions/continuous-integration/?hl=de

Josef

About the author

Josef

As much as possible as easily as possible and in the least possible amount of time. That's Josef's motto. Whether he's filling backends with data, testing apps or cutting videos. If there's a shortcut, he knows it and if there isn't one, he creates it. And when all the backends are full and all the apps are tested, you can find him browsing Steam, swinging his badminton racket or snapping pics with his Nikon.