Die JVMKit-Bibliothek von SoundCloud stellte gemeinsam genutzte JVM-Muster bereit – Service-Gerüste, Datenbank- und Kafka-Integration, Metriken – für Hunderte interne Dienste in Scala, Java, Clojure und JRuby. Mit wachsender Verbreitung verlor das Plattformteam die Sicht darauf, welche Dienste von welchen Teilen der JVMKit-API abhingen, wodurch jede Veröffentlichung ein Glücksspiel wurde: Ein routinemäßiges Update konnte stillschweigend Dienste beschädigen, von denen das Team nicht einmal wusste, dass sie betroffen waren.

Da Abhängigkeiten bei SoundCloud explizit in der Build-Datei jedes Repositorys deklariert wurden (sbt für JVM, Gradle für Android, npm für Web), indizierte das Team jedes Repository mit Zoekt, einer regexbasierten Code-Suchmaschine, die alle fünf Minuten aktualisiert wurde. Eine einfache Abfrage gegen Build-Dateien, die JVMKit als Abhängigkeit auflisten, erzeugte einen live umgekehrten Abhängigkeitsgraphen – jeden Dienst, der von einer JVMKit-Änderung betroffen wäre, automatisch generiert statt von Hand gepflegt.

Mit diesem Graphen zur Hand baute das Team eine Verifikationspipeline: Für jeden abhängigen Dienst das Repository klonen, es unverändert kompilieren und testen, um eine Baseline zu erstellen, dann die Kandidatenversion von JVMKit einsetzen, etwaige skriptgesteuerte Korrekturen für bekannte brechende API-Änderungen anwenden und erneut kompilieren und testen. Wenn die Baseline bestand, der Kandidat aber fehlschlug, war die JVMKit-Änderung die Ursache und nicht ein bereits vorhandener Bruch in diesem Dienst. Die Prüfung lief nächtlich über die gesamte abhängige Menge und bei jedem Pull Request gegen eine schnellere, auf einer Positivliste stehende Teilmenge.

Jeden Aufrufer kompilieren und testen, bevor die Bibliothek veröffentlicht wird

Es ersetzte einen manuellen, fehleranfälligen Prozess (veröffentlichen, warten, bis sich Teams beschweren) durch eine automatische, falsifizierbare Prüfung, die vor der Auslieferung der Veröffentlichung läuft.

Das Ausführen einer unveränderten Baseline zuerst isolierte, ob ein Fehler von der JVMKit-Änderung oder von einem Dienst stammte, der an diesem Tag bereits defekt war.

Die Aufteilung der Verifikation in einen nächtlichen vollständigen Lauf und einen schnellen, auf den PR beschränkten Lauf hielt das Feedback schnell, ohne die vollständige Abdeckung zu überspringen.

Der umgekehrte Abhängigkeitsgraph wurde aus Code generiert und nicht von Hand gepflegt, sodass er korrekt blieb, wenn Dienste hinzugefügt oder entfernt wurden.

Wenn eine interne Bibliothek unter Hunderten von Diensten liegt, sollte man nicht auf Hoffnung ausliefern – man sollte den Abhängigkeitsgraphen abfragbar machen und die CI jeden nachgelagerten Aufrufer zuerst testen lassen.

JVMKit betrieb weiterhin Hunderte von Diensten und Tausende von Produktionsinstanzen bei SoundCloud. Mit der eingerichteten Verifikationspipeline berichtete das Plattformteam, JVMKit häufiger zu veröffentlichen, wobei Fehler auf einen bestimmten Schritt und eine bestimmte Ursache zurückführbar waren, statt als unerklärliche Ausfälle nachgelagert aufzutauchen.

DEN BELEGEN FOLGEN

Die Quellen

  1. Did I Break You? Reverse Dependency Verification SoundCloud Engineering · developers.soundcloud.com ·