Softwareentwicklung auf einem höheren Level: Continuous Integration – Warum und wofür?
Die agile Softwareentwicklung versucht mit wenigen festgelegten Regeln, geringem Aufwand und meist einem iterativen Vorgehen, den Entwicklungsprozess voranzutreiben. Doch oft kommt es zu Problemen, wie instabile Build-Prozesse, schwierige Integration von Komponenten und unzureichende Testabdeckungen. Continuous Integration (CI) ist hier ein bewährtes Mittel zur Verbesserung und Optimierung des Softwareentwicklungsprozesses. Doch wie setzt man CI richtig ein und welche Regeln sind zu beachten? In diesem Artikel zeigen wir auf, wie der Einsatz von Continuous Integration dabei helfen kann, den Entwicklungsprozess zu optimieren. Zudem kann über entsprechende Analysewerkzeuge die Softwarequalität gesteigert und die Testabdeckung verbessert werden.
Einführung
Neue Softwarekomponenten entwickeln, Anwendungsteile integrieren und verlinken, Unit-Tests schreiben, Build-Prozess starten – fertig!? Der Softwareentwicklungsprozess funktioniert nicht nach diesem Schema und nicht so „einfach" wie beschrieben. In der Realität kommt es oftmals zu fehlschlagenden Builds, die nicht zuletzt auf mangelnde Testabdeckung zurückzuführen sind. Das Ganze spiegelt oft die Qualität der Software wieder. Gerade moderne Softwareprojekte gewinnen immer mehr an Komplexität und lassen sich immer schwieriger verwalten. Anwendungsteile werden unabhängig an verschiedenen Standorten und von unterschiedlichen Entwicklerteams implementiert. Entsprechend gibt es schon seit langem Ideen und Methoden, um den Softwareentwicklungsprozess zu optimieren und zu verbessern. Doch erst mit Martin Fowlers, im Jahre 2000 veröffentlichten Artikel „Continuous Integration", gewann das Thema unter dieser Definition zunehmend an Bedeutung. Seither werden in vielen Projekten Build-Server eingesetzt, um den Softwareentwicklungsprozess zu unterstützen, zu überwachen und zu automatisieren. In agilen Softwareentwicklungsprojekten ist Continuous Integration Pflicht!
Continuous Integration?
Die Frage, was genau Continuous Integration ist, kann nicht mit einem Satz beantwortet werden. Vielmehr lässt sich Continuous Integration als ein Vorgehen bezeichnen, das definierte Tools, Systeme und Methoden verbindet, um eine Automatisierung und Verbesserung des Entwicklungsprozesses zu erreichen. Das Ziel besteht darin, komplexe Softwareprojekte beherrschbar zu gestalten und damit gleichzeitig die Softwarequalität zu steigern. Continuous Integration soll dabei aber nicht nur helfen, das Softwareprojekt automatisiert zu kompilieren, sondern auch automatisiert Modul- und Integrationstests durchzuführen. Es gilt die Prämisse: Die Software befindet sich immer auf einem auslieferbaren Stand.
Abbildung 1 soll verdeutlichen, dass Continuous Integration eine Vielzahl von Elementen verbindet. Der Fokus aller Aspekte liegt immer auf der Optimierung des Entwicklungsprozesses und der frühzeitigen Erkennung von Fehlern bei der Integration von Anwendungsteilen. Darüber hinaus soll CI den effektiven Einsatz von Techniken und Werkzeugen unterstützen, um die Softwarequalität auf einem hohen Level zu halten. So können vor allem nicht funktionale Anforderungen, wie beispielsweise Wartbarkeit und Erweiterbarkeit, verbessert und so die Lebensdauer des Softwareproduktes verlängert werden.
Abbildung 1 zeigt auch Begriffe, wie Best Practice, Code Coventions und Code-Analyse-Werkzeuge, FindBugs, Checkstyle und SonarLint, die zunächst nur mittelbar mit Continuous Integration in Verbindung stehen. Aber gerade diese mittelbaren Komponenten des CI-Systems helfen komplexen Softwareprojekten, auch die Softwarequalität zu wahren und zu verbessern. Diese Plugins sollten mit Projektbeginn in den Entwicklungsumgebungen (aller Entwickler) installiert werden, um eine einheitliche Codebasis zu schaffen und so das Risiko möglicher auftretender Fehler bereits im Vorfeld zu minimieren.
Die genannten Werkzeuge erfüllen dabei unterschiedliche Aufgaben. Formatter dienen der einheitlichen Darstellung und Strukturierung des Sourcecodes. Checkstyle übernimmt die Prüfung des Programmierstils und erzwingt die Einhaltung von Codierrichtlinien, während FindBugs eine statische Codeanalyse zur Erkennung bekannter Fehlermuster durchführt.
Funktionsweise des CI-Systems
Der Build-Server (CI-Server genannt) fungiert als zentrale Integrationsinstanz und steuert den gesamten Build-Prozess. Als Beispiele für Build-Server seien an dieser Stelle CruiseControl, Hudson/Jenkins und Bamboo (Atlassian) genannt. Ein CI-System besteht neben dem Build-Server noch aus weiteren notwendigen Komponenten. Abbildung 2 zeigt die Hauptbestandteile eines CI-Systems.
Wie aus Abbildung 2 hervorgeht, wird der CI-Server mit einem Source-Code-Management-System verbunden, das den Quellcode des Projektes zentral verwaltet. In der Praxis handelt es sich hierbei oft um SVN- oder Git-Repositories. Der CI-Server erzeugt dabei ähnlich wie auf den Entwicklungsrechnern eine lokale Arbeitskopie des Projektes. In regelmäßigen Abständen schickt der CI-Server-Anfragen an das angebundene Versionsver-waltungssystem und prüft, ob es Änderungen am Quellcode gegeben hat. Ausgelöst durch Änderungen im SCM seit dem letzten CI-Build, wird ein neuer Durchlauf angestoßen. Dabei wird zunächst die lokale Arbeitskopie auf dem CI-Server aktualisiert (Update aus dem SCM) und anschließend das gesamte Projekte „gebaut". Dieser Vorgang beinhaltet das Kompilieren, Packen und das Ausführen automatisierter Unit-Tests. Der Build-Prozess wird dabei nicht vom Build-Server direkt ausgeführt, sondern von einem integrierten Build-Tool, wie beispielsweise Apache Ant, Maven oder Gradle.
Ein CI-System lässt sich um beliebige weitere Komponenten erweitern. So kann ein Code-Review-System, wie zum Beispiel BitBucket (Atlassian) oder Gerrit (bei Git als SCM), vorgeschaltet werden. Die Änderungen an der Software können so vor Integration in das Projekt nach dem „Vier-Augen-Prinzip" von einem oder mehreren Teammitgliedern überprüft werden. Erst wenn Änderungen freigegeben wurden, werden diese in das zentrale Repository übernommen und vom Build-Server abgegriffen und gebaut.
Der Continuous-Integration-Server liefert für jeden Build-Prozess einen Status zurück und meldet diesen an das Entwicklungsteam. Fehlerhaften Builds ist dabei immer, vor der Implementierung neuer Features, oberste Priorität einzuräumen!
--> Die Software ist wieder auf einen auslieferungsfähigen Stand zu bringen!
Grundprinzipien und Voraussetzungen für den Einsatz von CI
Die Verwendung von Continuous Integration ist an bestimmte Voraussetzungen gebunden. Um Continuous Integration erfolgreich zu betreiben, bedarf es der Einhaltung definierter Grundprinzipien:
- ein gemeinsames zentrales Repository
- automatisierte Build-Prozesse
- automatisierte Tests
- jede Änderung wird mit Integration in das Gesamtsystem gebaut und getestet
- Änderungen sollen regelmäßig und oft in das Gesamtsystem integriert werden
- der Build sollte stabil und schnell laufen
- produktionsnahe Umgebung für Testzwecke
- einfache Verwaltung aller Sourcen und Dokumente durch gemeinsame Repositorien und Wiki-Seiten
Die aufgeführten Punkte stellen zunächst einmal keine Neuheiten in der Softwareentwicklung dar. In den meisten Projekten finden Build-Tools und Repositorien bereits Verwendung. Vielmehr verfolgt CI den Ansatz, dass das genutzte zentrale Repository von einem Build-Server überwacht und regelmäßig gebaut wird und Modul- und Integrationstests durchgeführt werden. Das Feedback des Build-Servers hilft dem Entwicklungsteam, Fehler frühzeitig zu erkennen und auf das Fehlerverhalten der Software sofort reagieren zu können, wenn die Software sich nicht mehr in einem auslieferbaren Zustand befindet.
Es obliegt dem Entwicklerteam, keinen nicht funktionalen und ungetesteten Sourcecode einzuchecken. Das klingt zunächst banal, wird aber leider oft nicht beachtet. Weiterhin dürfen keine Änderungen eingecheckt werden, solange der lokale (Entwickler-) Build nicht stabil läuft und alle Fehler beseitigt sind. Fehlschlagende Builds haben immer Priorität vor der Implementierung neuer Features, da der Entwicklungsstand gefährdet ist.
Fungiert der CI-Server auch als Analysewerkzeug, müssen Entwickler ebenfalls kontinuierlich die erstellten Auswertungen beobachten und damit die Qualität der Software im Auge behalten.
Software-Qualitätsmanagement mit CI
Der CI-Server ist noch viel mehr als nur eine Code-Verwaltungszentrale zur Überwachung von Build-Prozessen. Wie im vorherigen Abschnitt angedeutet ist er ein zentraler Baustein, um ein umfangreiches Software-Qualitätsmanagement zu erreichen. Die automatisiert erstellten Build-Ergebnisse lassen sich mit Tools wie z.B. SonarQube automatisch analysieren. Mithilfe dieser Analysen können u.a. Aussagen zu Qualität und Testab-deckung der Software gemacht werden.
SonarQube erfindet das Rad hierbei nicht neu, sondern integriert bereits bekannte Funktionen aus Werkzeugen, wie Checkstyle, Findbugs, PMD und weitere. Die ausgewerteten Build-Ergebnisse werden an den SonarQube-Server übermittelt und auf einer Weboberfläche in Form von Metriken und Grafiken visualisiert. Die Entwicklung der Software bzgl. Qualitätsmerkmalen und Metriken lassen sich so über die Zeit hinweg vergleichen und helfen, unerwünschte Veränderungen zu erkennen.
Seit der SonarQube Version 4.3 lassen darüber hinaus sogenannte Quality-Gates bestimmen. Diese definieren, ob die aktuelle Version der Software einen bestimmten Reifegrad (qualitatives Level der Software) hat, der z.B. für ein Release ausreichend gut ist. Diese Quality-Gates können für Projekte auf Basis verschiedener Metriken definiert und angepasst werden.
In Verbindung mit der Nutzung eines SonarQube-Servers kann über das Plugin SonarLint die technische Bewertung der Softwarequalität „on-the-fly", während des Entwicklungsprozesses durchgeführt und eingesehen werden.
Der genutzte SonarQube-Server wird dabei in der Entwicklungsumgebung an das Projekt gebunden. Die Ergebnisse der SonarQube-Analyse werden direkt in der IDE angezeigt und die betroffenen Stellen im SourceCode markiert.
Ausblick
Aktuell lässt sich beobachten, dass die Anforderungen an moderne Softwaresysteme einem starken Wandel unterworfen sind. Höhere Flexibilität und kürzere Deploymentzyklen sollen für ein schnelleres „Time To Market" sorgen. Zu meist streben auch das Management und oft die Fachbereiche an, Softwarepakete, Produkte oder Änderungen häufiger und flexibler am Markt zu platzieren. Microservices sollen große und träge monolithische Systeme ablösen.
In diesem Zusammenhang haben sich Methoden, wie DevOps [1] und Microservices [2] etabliert, die einen Umbruch in der Prozess- und Team-Kultur mit sich bringen. Der DevOps-Gedanke sieht vor, dass ein Team für ein Produkt als Ganzes von der Planung bis zum Betrieb verantwortlich ist. Grenzen zwischen Entwicklung und Betrieb werden aufgelöst. In Konsequenz wird ein Prozess angestrebt, der eine Software qualitätsgesichert und höchstmöglich automatisiert und bis in die Produktion bringt. Im Kontext von Microservices werden sogenannte Querschnitt-Teams gebildet, die für einen definierten Themenkomplex komplett zuständig sind. Die Verantwortung wird damit auf ein Team verlagert und so Reibungsverluste, sowie Interessenkonflikte (Blame Game) an Abteilungsgrenzen vermieden.
Continuous Integration liefert als Ergebnis ein Stück funktionsfähige (ausführbare) Software. Eine sogenannte Continuous Delivery Pipeline erweitert nun den CI-Prozess um die Teststufen zur Qualitätssicherung. Dazu gehören z.B. Last- und Performance-Tests oder Acceptance-Tests. Möglich sind u.a. auch manuelle Smoke-Tests. Ziel bleibt immer ein möglichst hoher Automatisierungsgrad, auch wenn am Ende eine manuelle Freigabe für das Deployment in Produktionsumgebungen stehen kann.
Fazit
Continuous Integration ist ein bewährtes Mittel, komplexe Projekte beherrschbarer zu gestalten, und zwar unter Einhaltung von Regeln und Richtlinien sowie der notwendigen Disziplin von allen Projektbeteiligten. Nur so lässt sich der Entwicklungsprozess optimieren und die Softwarequalität durchweg kontinuierlich einhalten und verbessern. Continuous Integration ist kein Allheilmittel, jedoch als Basis für moderne Softwareentwicklungsprozesse heute unverzichtbar und sollte zum Standardrepertoire eines jeden Teams gehören.
Fehler halten sich nicht an einen Release-Plan und können immer auftreten! Continuous Integration soll jedoch das Risiko der Auslieferung von fehlerhafter Software minimieren. Eine möglichst vollständig getestete Software setzt viele Unit-Tests voraus, für die oft zu wenig Zeit eingeschätzt und aufgewendet wird. Des Weiteren lässt sich die konsequente Einhaltung aller Methoden und Regeln nicht immer mit den Projektanforderungen und Terminen vereinbaren. Doch die Verwendung von Plugins, wie Checkstyle, Findbugs und Code-Fromattern, funktioniert auch ohne Termine und Projektanforderungen. Solche Mittel und Tools sind notwendig, um die Software-qualität von vornherein zu schützen.
Dennoch ist es an den Entwicklern, die Verantwortung für den Code und für seine gewissenhafte Programmierung zu übernehmen. Das bedeutet auch, diesen zu testen. Unterschreiben Sie einen Vertrag, dessen Inhalt und Details Ihnen nicht vollumfänglich bekannt sind?
- Ein Commit in das SCM ist die Unterschrift eines Entwicklers, dass der Code nicht nur funktioniert, sondern auch lesbar, dokumentiert und ausreichend getestet ist -
Glossar
Blame Game
Als Blame Game werden Interessenkonflikte und gegenseitige Schuld zuweisen zwischen Abteilungen (oft Entwicklung und Betrieb), bei Produktivsetzung einer neuen Software/Software-Version bezeichnet.
CI
Continuous Integration beschreibt den Prozess des fortlaufenden Zusammenfügens von Komponenten zu einer Anwendung. Das Ziel der kontinuierlichen Integration ist die Steigerung der Softwarequalität. Typische Aktionen sind das Übersetzen und Linken der Anwendungsteile, prinzipiell können aber auch beliebige andere Operationen zur Erzeugung abgeleiteter Informationen durchgeführt werden. Üblicherweise wird dafür nicht nur das Gesamtsystem neu gebaut, sondern es werden auch automatisierte Tests durchgeführt und Softwaremetriken zur Messung der Softwarequalität erstellt. Der gesamte Vorgang wird automatisch ausgelöst durch Einchecken in die Versionsverwaltung.
VCS
Version Control System - Die Versionsverwaltung ist eine Form des Variantenmanagements; dort sind verschiedene Sprachvarianten oder modal auch anders bestimmte Varianten möglich.
Links/Quellen
[1] DevOps: https://de.wikipedia.org/wiki/DevOps
[2] Microservices: https://de.wikipedia.org/wiki/Microservices
[3] ORDIX seminare - Continuous Integration Workshop: https://seminare.ordix.de/seminare/entwicklung/course/1855-P-CI-01
[Q1] Flower, Martin (2006): Continuous Integration http://martinfowler.com/articles/continuousIntegration.html
Cover credit: http://www.tmplab.org/2015/06/13/one-liner-bash-based-continuous-integration-olbabaci/
Bei Updates im Blog, informieren wir per E-Mail.
Kommentare