Als ich diesen Artikel begann, schrieb ich eine Einführung für eines der Tools, das von unserem Frontend-Team verwendet wird – Create React App. Das Ziel war, zu erläutern, wie dieses Tool uns viel Zeit und Mühe bei der Auswahl, Installation und Konfiguration der verschiedenen unterstützenden Drittanbieter-Pakete erspart hat.
Im Laufe der Zeit kam ich zu dem Schluss, dass der erste Payt Software Engineering Artikel, anstatt in die Tiefe zu gehen, etwas allgemeiner sein darf. Ich habe mich daher entschieden, das Ziel dieses Artikels etwas anzupassen. Heute sprechen wir über verschiedene Maßnahmen in unserem Workflow, die die Produktivität des Engineering-Teams steigern und sicherstellen, dass wir uns voll und ganz auf die Entwicklung neuer Funktionen konzentrieren können.
Aber zuerst ein wenig Hintergrund zu unserem Software-Stack.
Payt bietet die umfassendste Software im Bereich Debitorenmanagement. Diese Software besteht aus einer Reihe von webbasierten Anwendungen. Hauptsächlich sind es die Debitorenmanagement-App und die Debitorenportal-App. Diese beiden Anwendungen werden mit Daten aus einer einzigen Backend-Anwendung „gefüttert“.
Die Backend-Anwendung von Payt besteht – zum Zeitpunkt des Schreibens – unter anderem aus den folgenden Technologien:
Frontend-Anwendungen der Payt-Anwendungen verwenden unter anderem diese Technologien:
Backend und Frontends kommunizieren über REST- und GraphQL-APIs und sind auf der Amazon AWS-Infrastruktur gehostet.
Payt existiert derzeit etwas länger als acht Jahre. In diesen Jahren ist das System gewachsen und erheblich erweitert worden mit verschiedenen Prozessen, Funktionen und der dazugehörigen Geschäftslogik. Die Wartung einer solchen Menge an Logik ist nicht trivial, aber sicherlich nicht unmöglich.
Testen, testen und nochmals testen
Es klingt vielleicht selbstverständlich, aber viele Unternehmen nutzen keine einzige Form von Tests in ihrer Software.
Die Entwicklung neuer Funktionen bei Payt geht immer mit der Durchführung von Tests einher. Diese Tests können auf verschiedene Weise durchgeführt werden. Bei Payt haben wir uns dafür entschieden, dass unsere Tests von Entwicklern geschrieben und durchgeführt werden. Das sogenannte Test-Driven Development. Bei der Entwicklung einer Funktion muss der Entwickler die sogenannten Unit-Tests schreiben. Diese Unit-Tests sind ein Teil des Codes. Wenn ein anderer Entwickler mit einer neuen Funktion beginnt und versehentlich die bestehende Funktionalität bricht, warnt die Test-Suite davor.
Git, Github und Reviews
Git ist ein Versionskontroll-Tool. Die am häufigsten genutzte Funktionalität von Git bei Payt sind die sogenannten „Branches“. Ein Branch ist ein Abzweig des bestehenden Produktcodes zur Entwicklung einer neuen Funktionalität in Isolation vom funktionierenden Produktionscode. Wenn die Funktionalität abgeschlossen ist und die Tests geschrieben sind, kann der Entwickler seine/ihre Arbeit zur Überprüfung anbieten. Github – eine Plattform für die Zusammenarbeit bei der Entwicklung über Git – bietet die Möglichkeit, Pull Requests zu erstellen. Ein Pull Request ist der Unterschied zwischen dem Haupt-Branch und dem Branch der neuen Funktionalität („Feature Branch“). Eine Voraussetzung für das Zusammenführen eines Pull Requests mit dem Haupt-Branch ist, dass die automatisierten Checks erfolgreich sind und ein oder zwei Kollegen den Code genehmigen.
Automatisierte Checks
Wie ich oben bereits erwähnt habe, sind die Tests Teil des Codes. Bei der Entwicklung eines Features kann ein Entwickler Tests durchführen, um sicherzustellen, dass der Code funktioniert. Unsere Test-Suite besteht zum Zeitpunkt des Schreibens aus 11.051 Tests. Einige davon testen die Integration zwischen den verschiedenen Komponenten und sind daher langsam. Es wäre eine große Zeitverschwendung, wenn wir vor der Entwicklung einer Funktionalität alle elftausend Tests ausführen müssten.
Deshalb haben wir ein externes System, das sicherstellt, dass jeder Git-Commit auf dem Haupt-Branch vollständig getestet ist. Ein solches System (Continuous Integration oder kurz CI) sorgt auch dafür, dass die Feature-Branches getestet werden, bevor sie von anderen Entwicklern überprüft werden.
Neben den Unit-, Akzeptanz- und Integrationstests gibt es auch eine Reihe anderer Checks, die sicherstellen, dass die Qualität des Codes gewährleistet ist. Einige davon sind:
- Überprüfung des Prozentsatzes des Codes, der durch Tests abgedeckt ist
Wir streben 100% an und liegen zum Zeitpunkt des Schreibens im Durchschnitt etwas über 90%.
- Überprüfung des Codestils
Die Codestil-Überprüfung stellt sicher, dass die Art und Weise, wie der Code geschrieben ist, einheitlich ist. Ein klarer und konsistenter Codestil erleichtert es neuen Teammitgliedern, sich mit dem Code vertraut zu machen. Aber auch die bestehenden Teammitglieder können schneller in einen unbekannten Teil der Anwendung eintauchen.
- API-Kompatibilitätsprüfung
Die Backend- und Frontend-Systeme bei Payt sind unabhängige Code-Basen, jeweils mit einem eigenen Git-Repository. Die Frontend-UI ist abhängig von den Daten, die über die API vom Backend bereitgestellt werden. Das Frontend erwartet auch eine bestimmte Datenstruktur. Diese Überprüfung stellt sicher, dass der Entwickler über die sogenannten „Breaking“ Changes in der API informiert ist.
Wenn einer der Checks nicht den Qualitätsanforderungen entspricht, muss der betreffende Entwickler die Fehler oder Unvollkommenheiten korrigieren. Seine oder ihre Arbeit darf bis dahin nicht in den Haupt-Branch ausgerollt werden.
Deployments
Entwickler wären keine Entwickler, wenn sie nicht all ihre Arbeit automatisieren würden. So ist auch die Bereitstellung der entwickelten Funktionen bei Payt automatisiert. Jede neue Funktionalität – getestet und genehmigt – wird vollständig automatisch auf unsere Server ausgerollt.
To do
Trotz der Menge an automatisierten Workflows hat das Engineering-Team von Payt immer noch viele Herausforderungen. Zum Beispiel, dass wir weniger Zeit mit der Konfiguration, Verwaltung und anderen sich wiederholenden und automatisierbaren Aufgaben verbringen müssen. Und stattdessen mehr Zeit für die Entwicklung neuer Funktionen und die Erweiterung bestehender Funktionen unserer Anwendungen aufwenden können.
So arbeiten wir beispielsweise derzeit an der Automatisierung des Aufbaus der Server, auf denen Anwendungen laufen. Die Absicht ist, dass unsere Infrastruktur im Code festgelegt wird. Damit haben wir bald eine Blaupause der Software, die für den Betrieb unserer Anwendungen erforderlich ist. Das beschleunigt die Bereitstellung neuer Server-Instanzen und sorgt für Konsistenz zwischen den verschiedenen Maschinen.