Arbeitsplatz Bild TGW Logistics Group

Speed Up Your Pipeline

Description

Thomas Svoboda von TGW Logistics Group teilt in seinem devjobs.at TechTalk einige Ansätze, wie man Azure DevOps Pipelines optimieren könnte.

Beim Videoaufruf stimmst Du der Datenübermittlung an YouTube und der Datenschutzerklärung zu.

Video Zusammenfassung

„Speed Up Your Pipeline“ von Thomas Svoboda zeigt, wie man Azure-DevOps-Pipelines beschleunigt, indem man sie szenariobewusst macht und per Variablen/Conditions unnötige Schritte wie Codeanalyse, Installer-Erstellung sowie NuGet Push/Promotion außerhalb von Release-Builds überspringt. Er demonstriert .NET-Build-Optimierungen – Lösungen konsolidieren, Projekte zusammenführen/entfernen und NuGet-Pakete nur bei Bedarf generieren – wodurch die Buildzeit von ca. 10 auf 6,5 und danach auf etwa 4 Minuten sank; zusätzlich rät er, langsame Unit-Tests zu überarbeiten, Tests nach Kritikalität zu bündeln und unabhängige Jobs/Stages parallel laufen zu lassen, wenn wenig Daten geteilt werden. So lassen sich Feedbackzyklen deutlich verkürzen und die Pipeline auf die häufigsten Use-Cases zuschneiden.

Azure DevOps-Pipelines beschleunigen: Erkenntnisse aus „Speed Up Your Pipeline“ von Thomas Svoboda (TGW Logistics Group)

Warum dieser Talk ins Schwarze trifft

Bei DevJobs.at sehen wir in nahezu jedem Engineering-Team dasselbe Muster: Continuous-Integration- und Delivery-Pipelines starten schlank und werden mit jedem neuen Feature, jeder Code-Qualitätsregel und jedem zusätzlichen Artefakt schwerfälliger. Das Ergebnis sind lange Feedback-Zyklen, sinkende Entwicklungsgeschwindigkeit und Frust in den Teams.

Im Vortrag „Speed Up Your Pipeline“ von Thomas Svoboda (TGW Logistics Group) ging es genau darum – mit sehr konkreten, pragmatischen Techniken für Azure DevOps. Was uns daran überzeugt hat: Der Fokus liegt nicht auf „magischen“ Tools, sondern auf nüchterner Analyse, klugen Entscheidungen pro Szenario und konsequentem Entfernen oder Bedingen von Arbeitsschritten.

Das Grundmuster jeder Pipeline – und wo sie aus dem Ruder läuft

Thomas startet mit einem einfachen, aber hilfreichen Raster, das wir in vielen Projekten genauso sehen:

  • Setup: Repository-Checkout, Variablen initialisieren, benötigte Tools installieren.
  • Build: Erzeugt die Artefakte, die in den nächsten Phasen benötigt werden.
  • Tests: Automatisierte Tests gegen die gebauten Artefakte.
  • Publish: Artefakte und Testergebnisse veröffentlichen.

So weit, so vertraut. Doch wie Thomas treffend formuliert, wachsen Pipelines „wirklich schnell“. Features kommen „left and right“ hinzu – und auf einmal ist die Feedbackzeit lang, die Produktivität leidet. Der Schlüssel ist daher nicht ein pauschaler Tuning-Trick, sondern die Frage: Welche Use Cases soll die Pipeline eigentlich bedienen – und welche Szenarien leiten sich daraus ab?

Szenarien denken statt Einheits-Pipeline

Thomas zeigt ein Beispiel aus der Praxis. Aus den vier Basisphasen war eine Pipeline entstanden, die zusätzlich:

  • umfangreiche Code-Qualitätsanalysen um die Phasen herum ausführt,
  • im Build .NET-Projekte als NuGet-Pakete erzeugt und diese anschließend in einem separaten Schritt in ein internes Feed pusht und promotet,
  • sowie am Ende aus allen Artefakten einen Installer bündelt.

Diese Gesamtorchestrierung ist sinnvoll – aber nur für einen spezifischen Use Case: den offiziellen Release. Und der passiert, wie Thomas sagt, nur „ein paar Mal im Monat“.

Daneben existieren zwei deutlich häufigere Szenarien:

1) Manuelle Builds für Entwickler

  • Ziel: schnell einen Installer aus einem Feature-Branch erstellen, z. B. für Integrations- oder Performance-Tests.
  • Wichtig: Geschwindigkeit und Verfügbarkeit des Installers.
  • Unwichtig: umfassende Code-Qualitätsscans, da diese ohnehin im Pull-Request folgen.

2) Pull Requests

  • Am häufigsten ausgeführt.
  • Enthält die vier Standardphasen – eingeschlossen von „extensive code quality analysis“.
  • Ziel: solide Qualitätsaussage bei akzeptabler Laufzeit.

Das führt zu einer Grundentscheidung: mehrere getrennte Pipelines pro Szenario – oder eine Pipeline, die ihr Verhalten erkennt und sich dynamisch anpasst. Thomas wählt die zweite Option: die Pipeline wird „szenariobewusst“.

Pipeline als Chamäleon: Variablen und Bedingungen

Der Weg zum adaptiven Verhalten führt über Variablen und Conditions. Thomas erläutert, wie die Pipeline anhand von:

  • Quellbranch (z. B. Feature-Branch vs. Main/Release),
  • Pull-Request-Kontext,
  • weiteren abgeleiteten Variablen (z. B. „Codeanalyse ausführen ja/nein“),

entscheidet, welche Schritte überhaupt ausgeführt werden.

Ein praktisches Beispiel ist der Code-Quality-Check (im Talk als „Sonocube analysis“ bezeichnet):

  • Die Analyse wird an eine Condition gebunden.
  • Löst die Bedingung zu „false“ aus, wird der Schritt komplett übersprungen.

Gleiches Muster, spürbarer Effekt:

  • Installer-Phase: besteht aus acht Schritten und kostet rund zwei Minuten. Wird per Condition deaktiviert, wenn sie im jeweiligen Szenario nicht benötigt wird.
  • Push/Promotion von NuGet-Paketen: Das ist ausschließlich für den offiziellen Release notwendig. Dieser Schritt dauert ca. vier Minuten – Einsparung in etwa 95% der Pipeline-Läufe.

Die Quintessenz: Statt jeden Run mit allem zu belasten, werden aufwändige Teile nur dann aktiv, wenn sie einen klaren Mehrwert liefern.

Den unvermeidlichen Kern beschleunigen: der Build

Es gibt jedoch eine Phase, die wir nicht „wegconditionen“ können: den Build selbst. Hier teilt Thomas zwei wirkungsvolle Maßnahmen aus einem .NET-Kontext:

1) Lösungen und Projekte konsolidieren

  • Ausgangslage: Vier .NET-Lösungen, die größte mit über 100 Projekten; Build-Dauer rund zehn Minuten.
  • Maßnahme: Die vier Lösungen zu einer zusammengeführt, kleinere Projekte zusammengelegt, veraltete Projekte entfernt.
  • Ergebnis: Reduktion von rund zehn auf etwa sechseinhalb Minuten.

2) NuGet-Packaging nur, wenn wirklich nötig

  • Ausgangslage: Für offizielle Releases müssen NuGet-Pakete gebaut werden. Eine gängige Praxis ist, die Paketgenerierung im Build zu aktivieren. Microsoft weist aber selbst darauf hin, dass dies die Build-Zeit erhöht.
  • Maßnahme: Die Paketgenerierung wird an einen Build-Parameter gekoppelt (etwa ein Ausgabepfad). Wenn dieser nicht gesetzt ist, wird nicht gepackt. Zusätzlich werden Unit-Test-Projekte grundsätzlich ausgenommen.
  • Ergebnis: Von den bereits reduzierten sechseinhalb Minuten weiter runter auf etwa vier Minuten.

Wichtig ist die Logik: Packaging einschalten, wenn es wirklich notwendig ist (z. B. offizieller Release); sonst aus. Testprojekte nicht packen. Diese beiden Grundregeln ziehen die Build-Zeit spürbar nach unten, ohne die Ziele der selteneren Szenarien (Release) zu gefährden.

Tests: schnell und relevant statt „alles immer“

Beschleunigung endet nicht beim Build. Thomas nimmt die automatisierten Tests ins Visier – mit zwei klaren, praxisnahen Empfehlungen:

1) Langsame Tests identifizieren und verbessern

  • In Visual Studio oder Rider lässt sich das Testergebnis nach Dauer gruppieren.
  • Aussage: „Ein einzelner Unit Test sollte niemals 100 Millisekunden überschreiten.“
  • Konsequenz: Tests, die diese Grenze reißen, gezielt überarbeiten. In großen Codebasen mit vielen tausend Tests summieren sich solche Verbesserungen stark.

2) Tests nach Schweregrad kategorisieren

  • Tests werden in Buckets organisiert (z. B. „kritisch“ vs. „erweitert“).
  • In Szenarien, die extrem schnelles Feedback erfordern (z. B. Pull Request), laufen nur die kritischen Tests.
  • Wenn Zeit keine Rolle spielt (z. B. dedizierte Nightly-Runs), können umfangreiche Performance- oder Erweiterungssuiten laufen.

Die Nachricht ist nicht: weniger testen. Sondern: zielgerichtet testen – an der Stelle und in der Tiefe, die das Szenario erfordert.

Parallelisierung: mächtig – aber nur, wenn die Voraussetzungen stimmen

Parallelisierung klingt nach dem Allheilmittel: Schritte in Jobs aufteilen, Jobs auf unterschiedliche Agents legen, mehrere Agents parallel nutzen. Doch Thomas warnt zurecht: Wenn Schritte stark voneinander abhängen und große Datenmengen austauschen müssen, frisst der Overhead den vermeintlichen Gewinn auf.

Wann es sich lohnt, zeigen zwei Beispiele aus seinem Alltag:

Beispiel 1: Zwei unabhängige Stages auf unterschiedlichen Agents

  • Setup: Zwei Stages mit je einem Job. Ein Job läuft auf einem Agent im TGW-Buildserver, der andere in Azure.
  • Eigenschaft: Beide sind vollständig unabhängig.
  • Kniff: In Stage 2 wird die Abhängigkeit entfernt („depends on empty array at stage two“). Beide Stages starten damit direkt.
  • Effekt: Vorher ~7 Minuten (je ~3,5 Minuten nacheinander), nachher ~3,5 Minuten (parallel).

Beispiel 2: Drei Stages, Stage 2 und 3 teilen sich Artefakte aus Stage 1

  • Setup: Stage 1 lädt Artefakte aus vorherigen Pipelines und legt sie in einem Shared Folder ab.
  • Abhängigkeit: Stage 2 und 3 brauchen nur Stage 1, nicht einander.
  • Kniff: In Stage 2 und 3 die Abhängigkeit explizit auf den Namen von Stage 1 setzen.
  • Effekt: Beide laufen parallel. In diesem konkreten Fall war der Zeitgewinn gering, da die Pipeline ohnehin schnell war – die Technik bleibt dennoch wertvoll.

Die Leitlinie: Parallelisieren, wenn die Schritte unabhängig sind und wenig Daten austauschen. Andernfalls gilt: erst entkoppeln, dann parallelisieren – oder es bewusst sequenziell lassen.

Praxisleitfaden: von der Analyse zur Umsetzung

Was nehmen wir als Redaktion in eine umsetzbare Reihenfolge mit? Aus dem Talk zeichnen sich fünf fokussierte Schritte ab:

1) Szenarien katalogisieren

  • Welche Use Cases existieren wirklich? Offizieller Release (selten), manuelle Entwickler-Builds (mehrfach täglich), Pull Requests (am häufigsten) – das war Thomas’ Dreiklang.
  • Für jedes Szenario Ziele und Muss-Kriterien festhalten (z. B. Installer nötig? Code-Qualität jetzt oder später?).

2) Pipeline-Verhalten bedingen

  • Relevante Variablen erfassen: Branch, PR-Kontext, abgeleitete Flags (z. B. „Codeanalyse ausführen“).
  • Teure Schritte mit Conditions versehen: Code-Quality-Analyse, Installer-Erstellung, NuGet-Push/Promotion.
  • Ziel: In ~95% der Läufe nicht benötigte Arbeiten überspringen.

3) Build verschlanken

  • Lösungen konsolidieren, doppelte/kleine Projekte zusammenlegen, Altlasten entfernen.
  • NuGet-Paketierung nur einschalten, wenn sie gebraucht wird. Testprojekte grundsätzlich ausnehmen.
  • Erwartbarer Effekt: Von ~10 Minuten auf ~6,5 und weiter Richtung ~4 Minuten – so Thomas’ Erfahrungswerte.

4) Tests fokussieren

  • Nach Dauer gruppieren, Ausreißer <100 ms bringen.
  • Schweregrad-Buckets definieren: „kritisch“ für schnelles Feedback; „erweitert/Performance“ für längere Läufe.

5) Parallelisieren mit Plan

  • Unabhängige Einheiten identifizieren, auf verschiedene Agents verteilen.
  • Abhängigkeiten bewusst modellieren (z. B. „depends on empty array at stage two“ oder „Stage 2/3 abhängig von Stage 1“).
  • Datenvolumen und Kopieraufwand realistisch bewerten.

Häufige Fallen – und wie Thomas sie umschifft

  • Alles immer ausführen: Wenn seltene Release-Anforderungen die tägliche PR-Experience diktieren, leidet die Entwicklungsgeschwindigkeit. Lösung: Szenario-gerechtes Aktivieren/Deaktivieren.
  • NuGet-Packaging standardmäßig im Build: Praktisch, aber teuer. Lösung: nur bei gesetztem Parameter (z. B. Ausgabepfad) und nie für Testprojekte.
  • Unkritische Tests in Hot Paths: Lässt Feedbackzyklen explodieren. Lösung: Schweregrade ernst nehmen und in schnellen Pfaden schlank bleiben.
  • Parallelisierung ohne Unabhängigkeit: Führt zu IO-Overhead und Synchronisationskosten. Lösung: Unabhängigkeit herstellen oder bewusst sequenziell bleiben.

Was uns besonders hängen geblieben ist

  • „Es gibt kein One-Size-Fits-All.“ Thomas betont, dass der Kontext zählt. Was im einen Team Minuten spart, ist im anderen irrelevant.
  • „Fokussiere das häufigste Szenario und mach es ‚fast as hell‘.“ Dieser Satz ist die vielleicht wichtigste Priorisierungshilfe. Optimiere, wo es die meisten Läufe betrifft – typischerweise der Pull-Request-Pfad.
  • Messbare Erfolge entstehen aus kleinen, aber klugen Entscheidungen: Installer-Phase skippen (2 Minuten), NuGet-Push/Promotion skippen (4 Minuten, in ~95% der Runs), Build konsolidieren (10 → ~6,5 → ~4 Minuten) – das summiert sich.

Fazit: Pipeline-Speed ist eine Produktmanagement-Aufgabe

„Speed Up Your Pipeline“ von Thomas Svoboda (TGW Logistics Group) ist ein Plädoyer dafür, Pipelines wie Produkte zu managen: mit klaren Zielgruppen (Szenarien), bewusstem Funktionsumfang (Conditions), konsequentem Aufräumen (Konsolidierung) und einem Sinn für echte Nutzerbedürfnisse (schnelles, relevantes Feedback).

Wer Azure DevOps nutzt, kann all das sofort anwenden – ganz ohne exotische Tools. Entscheidend ist, die Fragen aus diesem Talk ernsthaft zu stellen und ehrlich zu beantworten:

  • Welches Szenario ist am häufigsten – und wie bekommt es maximal schnelle Rückmeldung?
  • Welche Schritte sind teuer – und in welchen Runs überflüssig?
  • Wo blähen wir den Build künstlich auf – und wie machen wir ihn kompakter?
  • Welche Tests sind wirklich kritisch – und welche verlagern wir in langsamere Runs?
  • Wo lohnt echte Parallelisierung – und wo nicht?

Wenn diese Antworten in Variablen, Conditions und eine saubere Stufen-/Jobstruktur gegossen werden, kommt die gewünschte Beschleunigung fast zwangsläufig. Oder, um Thomas’ Schlussgedanken aufzugreifen: Es gibt kein Patentrezept – aber es gibt die eine goldene Regel, die immer trägt. Finde deine Szenarien. Optimiere das häufigste davon. Mach es „fast as hell“.

Weitere Tech Lead Stories

Weitere Dev Stories