Kontinuierliche Datenbankmigration mit Liquibase und Flyway

Der Quellcode der Applikation ist versioniert im Repository abgelegt. Warum nicht die Datenbank? Will man nun einen beliebigen Stand der Datenbank in der Entwickler-, Test- oder Produktionsumgebung wiederherstellen, stehen zwei Java-Bibliotheken zur Verfügung, die in ein Continuous Delivery integrierbar sind.

In Pocket speichern vorlesen Druckansicht 10 Kommentare lesen
Lesezeit: 16 Min.
Von
  • Niko Köbler
Inhaltsverzeichnis

Der Quellcode der Applikation ist versioniert im Repository abgelegt. Warum nicht die Datenbank? Will man nun einen beliebigen Stand der Datenbank in der Entwickler-, Test- oder Produktionsumgebung wiederherstellen, stehen zwei leistungsfähige Java-Bibliotheken zur Verfügung, die nahtlos in den Build für ein agiles Continuous Delivery integrierbar sind.

Ein Team entwickelt munter vor sich hin, ändert Code, checkt ihn in das Versionskontrollsystem (VCS; Version Control System) ein, zieht Updates von dort, aktualisiert die Entwicklungsumgebung und startet die Anwendung neu. Irgendwann ruft einer: "Wer zum Teufel hat den schon wieder die Datenbank geändert? Und warum sagt derjenige nicht, was er geändert hat? Jetzt läuft bei mir die Anwendung wieder auf einem Fehler, und ich muss meinen Datenbankzugriff anpassen!" Ärger und Aufregung, die das Team sich eigentlich ersparen will.

Im weiteren Verlauf des Projekts ist das Team gerade dabei, die Entwicklungen für das nächste Release abzuschließen. Den Code hat es eingecheckt, die Unit-Tests laufen alle. Bevor sich nun aber die Integrationstests durchführen lassen, ist die entsprechende Datenbank auf dem Integrationsserver noch zu aktualisieren, da, wie bereits schmerzhaft erfahren, strukturelle Änderungen im Laufe des letzten Sprints durchgeführt wurden und sich die Anwendung ohne eine Anpassung der Datenbank in der Integrationsumgebung nicht hochfahren lässt.

Doch was wurde in den letzten Wochen alles geändert? Wer weiß das noch alles und wer war an den Änderungen beteiligt und könnte zur erfolgreichen Aktualisierung der Integrationsdatenbank beitragen? Sind auch die Testdaten anzupassen? Nach einem Tag harter Arbeit und mühsamem Patchen der Datenbank, endlosem Hoch- und Runterfahren der Applikation, nervenaufreibendem Auswerten von Fehlerlogs und Stacktraces steht die Umgebung, und die Integrationstests laufen auch alle. Doch hätte sich das Team den gesamten Aufwand, den es womöglich an jedem Sprint-Ende treibt, nicht sparen können? Und hat es die jetzt durchgeführten Änderungen auch dokumentiert, um sie beim Kunden in der Produktionsumgebung genauso nachziehen zu können?

In einer anderen Situation ist ein Team angehalten, die Version A, die beim Kunden produktiv im Einsatz ist, auf einen Fehler hin zu untersuchen und diesen zu beheben. Doch es ist mittlerweile in der Entwicklung bei Version B angelangt und hat in der Zwischenzeit umfangreiche Änderungen an der Datenbank durchgeführt. Natürlich wurde nirgends so richtig dokumentiert, das Team war ja froh, dass die aktuelle Version läuft – und wer will schon auf eine alte Version zurück. Woher bekommt das Team jetzt das Layout der Datenbank, das zum entsprechenden Release der Version A passt? Den Code gibt es ja in der Versionskontrolle, man war ja schließlich nicht leichtsinnig. Aber die Datenbank? Warum ist die Datenbank nicht auch in der Versionskontrolle?

Die Datenbank darf nicht einfach nur als externer Speicher für Daten, mit der eine Applikation arbeitet, angesehen werden. Auch wenn sich ein Team noch so viel Mühe damit gibt, die Datenzugriffsschicht so weit zu abstrahieren und zu kapseln, dass es theoretisch jederzeit in der Lage wäre, die Datenbank unter der Applikation einfach auszutauschen. Somit könnte man vermuten, dass die Datenbank ja kein Teil der Applikation sein kann. Da eine Anwendung jedoch die Datenbank für das Management der anfallenden und benötigten Daten braucht, ist sie untrennbar mit ihr verheiratet, trotz Kapselung, Abstrahierung und theoretischer Austauschbarkeit. So, wie sich eine Anwendung in ihren Eigenschaften und Struktur weiterentwickelt, so entwickelt sich mit ihr auch die Datenbank weiter. Manchmal ändern sich nur die Daten, manchmal nur die Struktur, oft aber beides gleichzeitig. Die Anwendung wird oft als als eine Art Privatpatient angesehen, die Datenbank hingegen als Kassenpatient. Als Gegner einer 2-Klassen-Gesellschaft sollte man sich in seinen Projekten möglichst von Anfang an um das Wohlergehen und die Gesundheit der Anwendung inklusive der Datenbank kümmern.

Software-Teams sollten in der Lage sein, jederzeit einen bestimmten Zustand der Datenbank passend zu dem der Anwendung (wieder-)herstellen zu können – am besten automatisiert und auf Knopfdruck. Damit können sie erreichen, dass während der Entwicklung jeder Programmierer auf der aktuellen beziehungsweise korrekten Version der Datenbank parallel zum Anwendungscode arbeitet. Für das Deployment in Zielumgebungen wie Entwicklung, Test, Staging, Integration, Qualitätssicherung und schließlich die Produktion müssen sie sich dann keine Gedanken (und erst recht keine Sorgen) mehr machen. Weniger Ärger und Aufregung wären damit vorprogrammiert und damit auch eine stressfreiere Entwicklungszeit, die bestenfalls zu noch produktiveren und qualitativ hochwertigeren Ergebnissen führt. Wenn das Team parallel zu den strukturellen Änderungen der Datenbank noch passende Testdaten vorhalten und diese in der Test- beziehungsweise Integrationsumgebung automatisiert aktualisieren und für die Tests und damit für reproduzierbare Ergebnisse verwenden kann, dann ist es wirklich einen Schritt weiter und kann sich mehr auf das konzentrieren, was es eigentlich tun soll: Entwickeln, Verbessern, Erneuern – Mehrwert generieren.

Aber nicht nur die beschriebenen Vorteile beim Deployment lassen einen ruhiger werden. Checkt ein Team die Datenbank genauso wie den Quellcode in die Versionskontrolle ein, werden – funktionierende Continuous-Integration-Mechanismen vorausgesetzt – frühzeitig Fehler und Integrationsprobleme erkannt. Bevor unnötig viele andere Beteiligte auf die gleichen Fehler stoßen, ist es in der Lage, den Fehler schnell zu beheben und weiteren Frust im Team zu vermeiden. Nebenbei kann es mit dem Einchecken der Datenbank in das VCS noch für eine Art Backup und Dokumentation der Struktur sowie der gegebenenfalls vorhandenen Testdaten sorgen.

Bei der Recherche im Internet nach Best Practices über die Arbeit mit Datenbanken im Entwicklungsteam stößt man recht schnell auch auf die Website "Ode to Code" von K. Scott Allen. In seinem Blog schreibt Allen unter anderem auch über das Thema hier und hat drei Regeln für die Arbeit mit Datenbanken aufgestellt:

  1. Verwende niemals eine gemeinsame Datenbank für die Entwicklung im Team.
  2. Habe immer eine einzig maßgebliche Quelle für das Schema.
  3. Versioniere immer die Datenbank.

Punkt 1 hat seine Berechtigung, da die Fehler- und Frustrationsanfälligkeit extrem hoch ist, wenn ein Entwickler die gemeinsame Datenbank ändert und damit die anderen Teammitglieder nicht gleichzeitig informiert und mit entsprechend geändertem Code versorgt. Die beiden anderen Punkte zielen gleichermaßen auf die Erkenntnis, die Datenbank ebenfalls in das VCS einzuchecken und aus diesem die Zielsysteme zu aktualisieren und jederzeit eine für die Entwicklung oder für ein Bugfix geeignete Version wiederherzustellen. Die Datenbank muss also (hart gekoppelt) unter der gleichen Versionskontrolle stehen wie die Applikation selbst, da die Datenbank ein integraler Bestandteil der Anwendung ist.