Pareto Continuous Integration

Könnt Ihr unser Projekt auch so einrichten, dass der Build erst bei 80% fehlschlagenden Tests fehlschlägt?

Kürzlich fand ich mich mit dieser Frage konfrontiert. Bei aller Liebe musste ich mich kurz daran erinnern, dass es sicherlich einen gerechtfertigten Grund für diese Reaktion auf die kürzlichen Buildergebnisse gab. Alles in allem machte das aber nicht so wirklich Sinn für mich. Hierzu würde ich gerne eine etwas längere Erklärung geben.

Continuous Integration (CI) gehört anscheinend nach wie vor zu den am häufigsten missverstandenen Praktiken der XP-Kultur. Letzten Endes bedingt CI, dass ich einen automatisierten Build besitze, der mir innerhalb von 10 Minuten Feedback zu jeglichen Code-Änderungen liefert, und im Idealfall auch in der gleichen Zeit meine automatisierten Akzeptanztests ausführt. Im schlechtesten Fall laufen die Akzeptanztests in einem eigenen Staged-Build, der höchstens 3 Stunden dauern sollte.

Viele Teams bekommen das leider nicht hin. Dabei mangelt es vor allem an essentiellen Dingen wie einem vollautomatisierten Build (und damit meine ich nicht, die Konfiguration von Buildschritten in Jenkins), Unittests, die zu viel und zu unfokussiert testen, und schlechte oder gar keine Akzeptanztests.

10 Minuten Build

Ein Build sollte vollautomatisch durch einen Befehl ausgeführt werden können. Ein Kommando wie ant tests ist dabei genau so ein Befehl wie mvn release oder make -Dintegration. Nicht ok sind Builds, die ausschließlich aus der IDE oder dem Jenkins kommen, denn sie sorgen dafür, dass ich bei neuen Teammitgliedern viel Zeit in die Einarbeitung stecken muss. Außerdem führen nicht einheitliche IDEs dazu, dass ich stets Aufwand und Zeit in die Fehleranalyse stecken muss.

Zum gleichen Punkt zählt für mich auch, wenn ganze Buildketten ausschließlich über den CI-Server gebaut werden können, weil nirgendwo sonst die einzelnen Buildschritte abgebildet sind. Damit nimmt sich jedes Projektteam die Möglichkeit, auf Störungen im Build-System einzugehen, bzw. Änderungen lokal zu bauen. Klar, die Integrationsumgebung sollte einen definierten Stand haben, aber das Frontend-Team sollte nicht dadurch blockiert werden, dass der Build-Server wegen zu geringer Rechnerkapazitäten stundenlang benötigt, um einen Build vom Backend einzuspielen. In meinen Augen hat jeder ProductOwner an dieser Stelle ein gerechtfertigtes Interesse daran, ein professionelleres Arbeiten seines Entwicklungs-Teams zu verlangen.

Unfokussierte Unittests

Doch wie kommen überhaupt mehrstündige Builds zustande? Häufig sind sog. Integrationstests einer der Hauptgründe dafür. Ich persönlich hasse diesen Begriff, da bei dem Therm Integrationstest nicht klar ist, was integriert wird. Sind es einzelne Klassen? Module? Subsysteme? Alles auf einmal? Ich spreche hier von Tests, die lang sind, viele Abhängigkeiten besitzen, und mehr als eine Klasse heranziehen. Beim Editieren meines Kapitels zu How to reduce the cost of software testing erinnerte mich Michael Hill deshalb auch daran, dass wir Unittests besser Mikrotests nennen sollten – also sehr, sehr kleine fokussierte Tests. Diese bieten den Vorteil, dass sie schnell durchlaufen und im Idealfall innerhalb von Millisekunden Feedback liefern können.

Die Rechnung ist einfach. Nehmen wir an, wir haben eine Codebasis von 10.000 Klassen. 10.000 Klassen ist im Vergleich zu den meisten Projekten, die ich gesehen habe, nicht viel. Nehmen wir weiterhin an, dass diese 10.000 Klassen klein genug sind, um mit 20 Tests sinnvoll abgedeckt zu sein. Damit hätten wir 200.000 Tests für unser ganzes Projekt. Wenn die Tests jetzt im Durchschnitt eine Sekunden benötigen, dann bekommen wir eine Gesamttestsuite, die 55 Stunden zum Durchlaufen benötigt. Im Ernst: Würden Sie so eine Testsuite regelmäßig ausführen? Genau! Sie würden Sich im Blindflug befinden.

Wenn wir jetzt statt unfokussierter Integrationstests Mikrotests implementieren, dann bekommen wir schnelles Feedback. Nehmen wir mal 10 msek. pro Test an. Dann erhalten wir eine Testsuite, die in 2000 Sekunden durchläuft. Das sind ca. 33 Minuten. So eine Testsuite würde man doch schon etwas häufiger ausführen. Allerdings auch nicht bei jeder kleinen Änderung. Wenn wir die Zeit pro Mikrotest auf 1 msek. bekommen können, dann sind wir aber bereits bei ca. 3 Minuten. 3 Minuten, das klingt schon eher nach einer Testsuite, die ein Team regelmäßig ausführen würde.

Insgesamt könnte so ein Build auch unter 10 Minuten bleiben, inklusive dem Ausführen von sämtlichen Unittests. Aber dafür bedarf es schon einiger technischer Exzellenz. Letzten Endes muss das Team Testautomatisierung beherrschen, emergente Architekturen kennen und auch das Design muss eine testbare Flexibilität mitbringen. Ansonsten werden sie eher langläufige Intgrationstests schreiben können.

Fehlende Akzeptanztests

Wie Jerry Weinberg in Perfect Software …and other illusions about testing beschreibt, genügen Mikrotests allein nicht. Jeder der das glaubt, unterliegt der Decomposition Fallacy. Über Mikrotests hinaus benötigen wir Akzeptanztests, die das Zusammenspiel der einzelnen Komponenten durchspielt. Dabei wollen wir uns auf die fachlichen Begriffe fokussieren, anstatt der eher technisch fokussierten Mikrotests. Akzeptanztests und Mikrotests formen ein symbiotisches Sicherheitsnetz, das es dem Team ermöglicht, kontinuierlich neue Features in gleichbleibender Geschwindigkeit zu liefern.

Meistens benötigen Akzeptanztests länger als Mikrotests. Mit einem idealen Gemisch aus Akzeptanz- und Mikrotests gelingt mir aber der Trade-Off zwischen technischer Exzellenz und fachlichen Anforderungen. Während mir die Mikrotests schnelles Feedback geben, benötige ich aus fachlicher Sicht nur noch wenige Akzeptanztests, die mir die Lücken End-to-End schließen. Eine breite Testbasis aus Mikrotests hilft mir also dabei, auch weniger langläufige Akzeptanztests auf mich zu nehmen. Im Idealfall kann ich 95% Code Coverage erreichen, während mein gesamter Build nach wie vor unter 5 Minuten benötigt. Wie häufig würden Sie in einem solchen System Ihre Tests ausführen? Wie schwer wäre es, wenn Sie Sich in so eine Codebasis neu einarbeiten müssten? Denken Sie mal drüber nach, was 5 Minuten bis zum Feedback darüber, ob ich gerade einen Fehler in das System eingebaut habe, hier bedeuten können.

Langer Rede, kurzer Sinn: Wenn ich meine Programmierer derart unter „Feature-Druck“ setze, dass sie die Scheuklappen für Neulieferung von Features aufsetzen, dann muss ich mich nicht wundern, wenn Dinge wie technische Exzellenz, gute Unittests und ein 10-Minute Build auf der Strecke bleiben. Das mag zwar kurzfristig Vorteile haben, langfristig baue ich mir aber einen Haufen technische Schulden auf, die mir mittelfristig eine steile Aufwandskurve bringen werden. Der einzige Weg, schnell voranzukommen, ist eben doch der Weg, dass ich vernünftig voranschreite.

  • Print
  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Yahoo! Buzz
  • Twitter
  • Google Bookmarks

Ein Gedanke zu „Pareto Continuous Integration“

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert