Continuous Integration widerspricht Feature Branches!

Continuous Architecture  –  20 Kommentare

Features Branches sind ein beliebter Ansatz, um die Entwicklung unterschiedlicher Features zu trennen – nur leider widerspricht das dem Ziel von Continuous Integration, ständig alle Änderungen zu integrieren. Was also tun?

In den 90er-Jahren habe ich einen Kunden für einen Tag beraten. Letztlich habe ich aber den größten Teil des Tages damit verbracht, jemandem zuzuschauen, wie er die Software zu kompilieren versuchte. Das ist heute kein Problem mehr – selbst chaotische Projekte können einen relativ aktuellen Stand der Software vorzeigen, der gerade kompiliert worden ist.

In einem größeren Projekt, an dem ich mitarbeitete, gab es Schwierigkeiten auf einer anderen Ebene: Die Teams bauten jeweils eigene Module. Vor dem Release war regelmäßig eine Task Force damit beschäftigt, die Module der verschiedenen Teams zu integrieren. Allein alle Module zum Kompilieren zu bringen, hat oft schon Wochen gedauert. Heute werden in den meisten Projekten alle Änderungen aus der Versionskontrolle von allen Teams regelmäßig ausgecheckt, kompiliert und getestet. Dadurch können solche Probleme nicht mehr auftreten. Die Änderungen aller Teams werden ständig gemeinsam erprobt. Man spricht von Continuous Integration (kontinuierlicher Integration): Alle Änderungen aller Teams werden ständig integriert.

Continuous-Integration-Server wie Jenkins oder TeamCity sind Werkzeuge, um genau diesen Prozess zu automatisieren. Die Änderungen an einem bestimmten Zweig werden aus der Versionskontrolle ausgecheckt, kompiliert und die Unit-Tests werden laufen gelassen.

Feature Branches

Feature Branches sind ein Muster, um mit einer Versionskontrolle die Entwicklung von Features zu trennen. Entwickler erstellen neue Features in einem Branch in der Versionskontrolle. So lässt sich jedes Feature getrennt entwickeln. Mit modernen Werkzeugen wie Git ist das wesentlich einfacher als mit vielen älteren Werkzeugen. Git ist ursprünglich als Werkzeug für die Entwicklung von Linux gestartet. Dort wird das Branching genutzt, um die Entwicklung neuer Features zu isolieren und auch Bugfixes getrennt zu entwickeln. Erst nach einer Review werden die Änderungen tatsächlich übernommen.

Feature Branches isolieren also bestimmte Änderungen. Und genau das ist das Problem. Änderungen aus den verschiedenen Feature Branches werden nicht mehr integriert. Erst wenn der Branch in den Hauptzweig übernommen wird, werden die Änderungen aus dem Branch mit den anderen Änderungen integriert. Und erst dann treten auch möglich Konflikte und Probleme zu Tage. Es ist demnach eben nicht mehr Continuous Integration – also fortlaufende oder ständige Integration. Stattdessen gibt es eine Integration zu den Zeitpunkten, wenn die Feature Branches wieder eingestellt werden.

Man könnte nun einwenden, dass in gewisser Weise auch die Arbeit von Entwicklern zwischen den Commits Continuous Integration widerspricht. Wer wochenlang seine Änderungen nicht committet, wird auch Integrationsprobleme haben. Insofern sind das auch Branches. Das führt in der Praxis aber kaum zu Problemen, da sich häufige Commits mittlerweile durchgesetzt haben.

Branches kann es auch noch aus anderen Gründen geben – beispielsweise für Prototypen, Spikes oder um etwas auszuprobieren. Dann kann die Isolation eines eigenen Branches besser sein, als dass die Änderungen in den Hauptzweig einfließen und später dann gegebenenfalls wieder entfernt werden müssen.

Was nun?

Feature Branches können funktionieren. Viele Teams haben gute Erfahrungen gemacht. Die Implementierung in den Branches sollte nicht zu lange dauern, damit die Konflikte nicht zu groß werden. Ebenso sollte die Anzahl der Branches nicht zu groß werden. Zwischen den Branches sollte Code regelmäßig ausgetauscht und Code aus dem Hauptzweig in die Branches übernommen werden. Aber es bleibt ein Widerspruch zu Continuous Integration: Die Änderungen werden eben nicht alle integriert. Ein Continuous-Integration-Server für jeden Feature Branch hilft auch nicht. Er kann zwar überprüfen, ob die Änderungen an dem Branch kompilieren und sie auch testen – aber die Integration mit den anderen Branches ist gezwungermaßen mit diesem CI-Server nicht möglich.

Continuous Delivery verschärft das Problem: Neben der Integration, dem Kompilieren und den Test der Continuous-Integration-Server kommen weitere Test-Stufen zu der Pipeline hinzu, bis de Software schließlich in Produktion geht. Jeden Branch mit einer vollständigen Continuous Delivery Pipeiline auszustatten, ist nicht nur aufwendig, sondern die Pipeline muss gezwungenermaßen auch bei den Branches irgendwo vor der Produktion beendet werden – schließlich kann nur ein Branch in Produktion gehen.

Feature Toggles

Dennoch muss es auch mit Continuous Integration irgendwie möglich sein, Features zu entwickeln, ohne dass sie gleich in Produktion gehen. Dazu gibt es Feature Toogles. Im Prinzip bedeutet das lediglich, dass neue Features deaktiviert werden können, damit sie nicht in Produktion genutzt werden können – im Prinzip sind das einfache Fallunterscheidungen. Dadurch lassen sich neue Features ohne Branches entwickeln. Sie werden alle gemeinsam im Hauptzweig implementiert. In Test-Umgebungen können Features aktiviert werden, während sie in Produktion noch deaktiviert sind.

Feature Toggles haben auch noch weitere Vorteile: So wird der Live-Gang eines neuen Features vom Deployment entkoppelt. Die Software kann deployt werden, das Feature kann zunächst deaktiviert sein und dann zu einem bestimmten Zeitpunkt aktiviert werden. Das macht den Umgang mit Deadlines für bestimmte Features deutlich einfacher. Die Software kann schon lange vorher in Produktion sein, zum entscheidenden Datum wird nur ein Feature Toggle anders gesetzt.

Feature Toggles sind eigentlich einfach. Aber Probleme gibt es dennoch: Irgendwann muss der Code von überflüssigen Feature Toggles befreit werden [-] aber wann? Und natürlich muss gewährleistet sein, dass immer die richtigen Feature Toggles in Produktion an beziehungsweise aus sind. Außerdem wird das Konzepte oft missbraucht: A/B Testing, bei denen eine Benutzer-Gruppe ein Feature nutzen kann und eine andere nicht, ist zwar ähnlich, dient aber einem anderen Ziel. Eine Vermischung macht die Handhabung der verschiedenen Konfigurationen in Test und Produktion noch komplizierter.

Fazit

Continuous Integration widerspricht Feature Branches. Continuous Integration zielt auf ständige Integration aller Änderungen ab, Feature Branches auf Isolation und entkoppelte Entwicklung von Features. Daraus lässt aber nicht unbedingt ableiten, dass eines der Vorgehen besser ist. Wer heute mit Feature Branches erfolgreich arbeitet und keine Integrationsprobleme hat, kann durchaus mit diesem Ansatz weiterarbeiten. Continuous Integration, Feature Branches und viele weitere Konzepte sind am Ende nur Werkzeuge, aus denen Teams sich ihren persönlichen Werkzeugkasten zusammenstellen können. Und Feature Toggles sind auch nicht ohne Herausforderungen.

PS: Danke an die innoQ-Kollegen Philipp Haußleiter, Alexander Heusingfeld, Willem van Kerkhof, Andreas Krüger, Timo Loist und Philipp Schirrmacher für die Diskussion über eine frühe Version des Blog-Post!