Troubleshooting karma (node) performance problems

After some months of nothing but backend development I started to work on my customers Angular frontend application again. I noticed that starting a single karma test took way longer than it used to be. A simple test for a pure function in headless chrome took about half a minute, in which the actual test only took some milliseconds. That was really annoying, because in my current refactoring task I have to execute lots of tests very often. Searching, browsing the karma issues and so on didn’t help, so I want to describe, how I found out what has been causing the problems. 

Weiterlesen

Metriken, Alerts und die Nadel im Heuhaufen

Bei meinem Kunden haben wir einige Schleifen mit Metriken und Alerts hinter uns. Die ersten Metriken und Alerts haben am Anfang dazu geführt, dass die Anwendung nur noch mit hohem manuellen Aufwand betreibbar war, da man quasi den einen wichtigen Alert in dem Haufen von anderen ggf. unwichtigeren Alerts nicht gefunden hat. Die Erfahrung und unsere aktuelle Lösung möchte ich hier gerne weitergeben.

Weiterlesen

Custom protocol mapper zur Anpassung des Keycloak JWT Tokens

Per Default schreibt Keycloak viele Dinge in den JWT token, wie zum Beispiel den bevorzugten Benutzernamen. Wenn das nicht genug ist, gibt es viele bereits eingebaute so genannte protocol mappers mit denen der JWT token, erzeugt von Keycloak,  noch mehr angepasst werden kann. Manchmal sind die eingebauten protocol mappers nicht genug. In diesem Fall können selbstgeschriebene protocol mapper Keycloak hinzugefügt werden. Ich habe hierfür eine Beispielprojekt bei github erstellt.

Leichgewichtige Architekturvalidierung mit ArchUnit

In meinem vergangenen Projekt mit mehreren zehntausend Zeilen Code fand ich jQAssistant gut, um einen Überblick über den Code zu bekommen und Architekturregeln einfach zu validieren. Siehe dazu auch meinen vorherigen Blogpost.

Aktuell arbeite ich in einem Projekt, das vor zwei Jahren auf der grünen Wiese gestartet ist. Dieses besteht aus mehreren Mikroservices und einem Angular Frontend. Nun ist es allerdings so, dass bei einem unser wichtigen größeren Services (ca. 7000 LOC), in dem sehr viel Fachlichkeit steckt, mir immer häufiger Verletzungen unserer hexagonalen Architektur aufgefallen sind – bspw. Zugriffe von Domänen Klassen auf Klassen des REST Ports.  Dabei handelte es sich häufig um Flüchtigkeitsfehler. Die Setupkosten von jQAssistant waren mir bisher für diese kleinen Services zu hoch. Daher habe ich nach einer „leichgewichtigen“ Alternative gesucht mit der ich des Wildwuchses Herr werden konnte und bin dann bei ArchUnit fündig geworden.

Weiterlesen

Nie wieder mühsam Code nach Mustern durchsuchen

Letztes Jahr habe ich den empfehlenswerten Steering Agile Architecture Kurs von Tudor Girba bei it-agile besucht. Der Aha-Effekt in diesem Kurs kam für mich, als es hieß, die gängige Vorgehensweise bei der Softwareentwicklung sei auf dem Stand der Technik von 1939. Damals wurden vier Millionen Fahrkarten unter immensen Kosten von Hand analysiert, um die Weiterentwicklung des Londoner U-Bahn-Systems zu steuern. Heute würde man dieselben Informationen innerhalb von Sekunden von Servern berechnet bekommen. Laut Tudor Girbas These ähnelte die aktuelle Vorgehensweise in der Softwareentwicklung sehr dem Zählen der Fahrscheine 1939. Um die Gemeinsamkeiten und mögliche Gegenmaßnahmen soll es in diesem Blog Beitrag gehen.

Weiterlesen

Voice user interface design mit Amazon Alexa

Ich war kürzlich auf einem von Amazon veranstalteten ganztägigen Workshop zum Thema Alexa hier in Hamburg und vorher auch schon mal bei einem Meetup der AWS User Group zu demselben Thema. Die Quintessenz aus beiden ist, dass die Prinzipien nach denen ein Voice User Interface (bspw. mit Alexa) entwickelt werden sollte, grundlegend von den Prinzipien nach dem bspw. eine Weboberfläche designed wird, abweicht. Eine Weboberfläche eins zu eins in eine Voice UI zu übersetzen, funktioniert nicht. Um einige dieser grundlegenden Prinzipien soll es im Folgenden gehen.

Weiterlesen

Hypermedia Links einer REST API in einem Client als ID nutzen

Schreibt man einen HTML Client für einen REST Api, so stellt sich die Frage, wie man die Urls des HTML Clients aufbaut. Auch die Url im HTML Client sollte dabei eindeutig zu einer Resource passen, damit man den Links auf die Seite des HTML Clients verschicken kann. Man muss also aus der Url des HTML Clients geschickt die Url der REST Api ableiten können. Darum soll es im Folgenden gehen.

Weiterlesen

Integration von Self-Contained Systems

Um das Frontend mehrerer Self-Contained Systems auf einer Seite zu integrieren, gibt es verschiedene Strategien. Im April war ich bei einem ganz interessanten Meetup der Java User Group Hamburg zu dem Thema „VueJS und Microservices für myhermes“. In diesem haben die Entwickler von Hermes vorgestellt, was für Probleme sie hatten und wie sie die mit ihrer neuen Self-Contained Systems Architektur gelöst haben die demnächst produktiv gehen und ihr monolithisches Altsystem ablösen soll. Interessant fand ich dabei, wie sie die Frontends verschiedener Self-Contained Systems auf einer Seite integrieren.

Weiterlesen

BDD UI Tests mit Serenity und JBehave

In meinem aktuellen Projekt schreiben wir die UI Tests als BDD Tests, da diese eher dazu führen, dass man fachliche Tests schreibt. Dazu verwenden wir Serenity zusammen mit JBehave.  Serenity verwendet dabei unter der Haube Selenium für die Browserautomatisierung. Dabei haben wir in meinem Team einen längeren Leidensweg hinter uns da zwischenzeitlich die Tests so schwer zu warten waren, dass wir mit dem Gedanken gespielt haben, die ganzen BDD /JBehave Tests wegzuschmeißen und durch reine Java Tests zu ersetzen. Letztendlich sind wir aber nach langer Zeit (dank Beratung eines anderen Teams) nun endlich bei einer Lösung angekommen, wo alles zusammenpasst und wir nun sehr zufrieden mit unseren UI Tests sind. Darum soll es in diesem Artikel gehen.  Weiterlesen

Klar formulierte Tests

In der Vergangenheit habe ich häufig Tests gesehen (und auch selber geschrieben), die schwer zu warten waren, da sie nicht klar formuliert waren. Nehmen wir beispielsweise folgenden (zugegebenermaßen noch recht einfachen) Test als Beispiel:

final Booking DOOR2DOOR_BOOKING = aBooking().withDefaultValues()
            .withOriginLocation(aLocation().withDefaultDoorValues().build())
            .withDestinationLocation(aLocation().withDefaultDoorValues().build())
            .withIncoterm(IncotermType.CIP)
            .withPackageDetails(aPackageDetails().withDefaultValues().build())
            // ... hier werden noch viele weitere Felder gesetzt
            .build();

@Test
public void shouldConvertTotalWeight() {
    // when
    final PackageDetailsComponent actual = converter.createPackageDetails(DOOR2DOOR_BOOKING);
 
    // then
    assertThat(actual.getTotalWeight()).isEqualTo(DOOR2DOOR_BOOKING.getTotalWeight());
}

// viele weitere Tests die auf DOOR2DOOR_BOOKING zugreifen

Der obige Test drückt sich nicht klar aus.  Dadurch könnten selbst bei einem so einfachen Test folgende Fragen bei dem Leser des Tests aufkommen:

  • Auf Grund des Namens DOOR2DOOR_BOOKING könnte sich der Leser fragen, warum ein Door to Door Booking benötigt wird, damit das Feld totalWeight konvertiert werden kann.
  • Wenn sich der Leser die Initialisierung von DOOR2DOOR_BOOKING ansieht, könnte er ziemlich verwirrt werden, da dieses Objekt auch noch von anderen Tests verwendet wird, die andere Felder von DOOR2DOOR_BOOKING  wie bspw. das Feld incoterm benötigen. Er muss daher recherchieren, welche dieser Felder für den Test wichtig sind. Es könnte bspw. sein, dass das Feld totalWeight nur in bestimmten Fällen kopiert wird. Das ist schwer offensichtlich wenn die wichtigen Felder, die für den Test gesetzt sein müssen, in der Masse der für den Test unwichtigen Felder (wie bspw. incoterm), untergeht.
  • Da das Objekt, das auf DOOR2DOOR_BOOKING zugewiesen wurde, von mehreren Tests verwendet wird, müsste man prüfen, ob es immutable ist. Wenn nein, so könnte es von anderen Tests verändert werden, was zu instabilen Tests führen kann und zusätzlich verwirrt.
  • Auch würde er sich vielleicht fragen, ob der Test überhaupt etwas testet, da der Test auch durchlaufen würde, wenn das Feld totalWeight sowohl in actual als auch in DOOR2DOOR_BOOKING null wären.

Für viele dieser Fragen würde man Vermutungen anstellen. Ich würde beispielsweise vermuten, dass das Feld einfach (unabhängig von irgendwelchen anderen Feldern) kopiert wird. Auch würde ich vermuten, dass das Feld totalWeight mit einem Wert initialisiert ist und somit auch durch die Zeile assertThat(actual.getTotalWeight()).isEqualTo(DOOR2DOOR_BOOKING.getTotalWeight()) auch wirklich geprüft wird, dass ein Wert kopiert wird. Eventuell wird der Leser sogar in den Code abtauchen, um seine Vermutungen zu verifizieren. Das ist schlecht, denn eigentlich möchte man nicht, dass sich der Leser unnötige Fragen stellt. Stattdessen sollte jeder Test für den Leser offensichtlich sein. Er sollte die Tests einer Klasse überfliegen und sehr schnell darüber erfassen können, wie eine Klasse funktioniert.

Schreibt man den Test nun ein wenig um, so kommuniziert der Test deutlich klarer, was wichtig ist:

@Test
public void shouldConvertTotalWeight() {
    // given
    final PreciseAmount<Mass> weight = PreciseAmount.valueOf(120, SI.KILOGRAM);
    final Booking booking = someBooking().withTotalWeight(weight).build();

    // when
    final PackageDetailsComponent actual = converter.createPackageDetails(booking);

    // then
    assertThat(actual.getTotalWeight()).isEqualTo(weight);
}

Der Leser sieht hier sofort, dass wir nur irgendein Booking benötigen, bei dem das Feld totalWeight gefüllt ist. Dieses kommuniziert das Builder Pattern someBooking().withTotalWeight(weight).build(). Die Methode someBooking() baut irgendein Booking zusammen, so dass der Aufruf der Methode createPackageDetails(booking) nicht zu einer Exception führt. Danach werden dann die Felder überschrieben, die für unseren Test wichtig sind. Im obigen Fall wird lediglich das Feld totalWeight im Booking gesetzt. Damit wird an den Leser kommuniziert, dass nur dieses eine Feld für den Test wichtig ist. Er sieht also auf den ersten Blick, dass dieses eine Feld in diesem Test lediglich kopiert wird. Auch ist nun klar, dass das booking.totalWeight ungleich null ist und somit auch wirklich kopiert wird. Weiter erzeugt sich nun jeder Test ein neues Booking-Objekt so dass kein anderer Test es verschmutzen kann selbst wenn es mutable ist.

Das war nun noch eine ziemlich einfache Methode, die nur ein Feld kopiert. Wird die Methode die getestet wird und der zugehörige Tests komplizierter, ist es noch wichtiger drauf zu achten, dass der Test verständlich ausgedrückt ist. Ansonsten muss man später bei Anpassungen am Produktionscode zusätzliche Zeit aufwenden, um überhaupt erst einmal die Tests zu verstehen und zu refaktorisieren.