GitHub Actions tötet Engineering-Teams langsam
(iankduncan.com)- Wird weithin genutzt, weil es als standardmäßig im Repo enthaltenes CI gilt, aber strukturelle Ineffizienz und eine instabile User Experience senken die Produktivität von Entwicklern
- Langsames Laden des Log-Viewers und Browser-Abstürze sowie komplexe YAML-Syntax und Expression-Fehler führen zu wiederholtem Debugging
- Durch eine Architektur ohne Besitz der Compute-Ressourcen zeigen sich Grenzen bei Performance, Skalierbarkeit und Kontrolle über die Umgebung
- Beim Versuch, viele Probleme zu umgehen, entsteht immer wieder die Situation, dass man mit komplexem YAML oder riesigen Bash-Skripten das CI selbst neu baut
- Im Vergleich dazu bietet Buildkite mit einer einfachen YAML-Struktur, selbst hostbaren Agents und einer praxistauglichen Log-Erfahrung eine langfristig wartbare CI-Alternative
Die Probleme von GitHub Actions
- Der GitHub-Actions-Log-Viewer ist ineffizient: Schon zum Prüfen eines einfachen Fehlers sind mehrere Klicks und Seitenladevorgänge nötig
- Bei einem fehlgeschlagenen Build braucht man von der Check-Zusammenfassungsseite → zur Workflow-Ausführungsseite → zur Job-Seite → bis zum Klick auf den eingeklappten Step 3–4 Seitenwechsel, jeweils mit separatem Laden
- Bei großen Build-Logs bringt er den Browser wiederholt zum Absturz, und beim Nutzen der Suchfunktion friert Chrome reproduzierbar ein
- Bei langen Logs funktioniert sogar das Scrollen nicht mehr, sodass man am Ende das rohe Log-Artefakt herunterladen und in einem Texteditor öffnen muss
- Der Zurück-Button führt nicht zur ursprünglichen PR-Seite zurück, sondern auf unvorhersehbare GitHub-Actions-UI-Seiten, und der Browser-Verlauf füllt sich mit Actions-URLs
- Unproduktiver Debugging-Prozess
- Um Umgebungsvariablen zu prüfen, fügt man einen
run: env-Step hinzu und pusht erneut, was zu einer Feedback-Schleife von 20 Minuten führt; bei einer einzeiligen Änderung wiederholt sich dieser Vorgang schnell ein Dutzend Mal - Wiederholte 20-Minuten-Feedback-Schleifen führen dazu, dass ein ganzer Arbeitstag in Wartezeit auf CI aufgeht
- Um Umgebungsvariablen zu prüfen, fügt man einen
- Strukturelle Grenzen von YAML
- GitHub-Actions-YAML ist eine spezielle Form, in der eine eigene Expression-Sprache, ein Kontext-Objektmodell und Regeln für String-Interpolation kombiniert werden
- Wenn man in einem
${{ }}-Ausdruck nur ein Anführungszeichen falsch setzt, merkt man oft erst nach 4 Minuten Wartezeit auf den Start des Runners, dass ein String verloren gegangen ist - Die Expression-Syntax liegt in einem Grenzbereich (liminal space): zu komplex für Konfiguration, zu eingeschränkt für eine echte Programmiersprache
- Die Syntax wird nicht über die Dokumentation, sondern über Fehlversuche gelernt
- Sicherheitsrisiken des Marketplace
- Wenn über die Syntax
uses:externe Actions eingebunden werden, gibt man unverifizierten Dritten Zugriff auf Repository, Secrets und Build-Umgebung - SHA-Pinning ist zwar möglich, wird aber kaum praktiziert; und selbst dann führt man undurchsichtigen, ungelesenen Code zusammen mit Zugriffsrechten auf
GITHUB_TOKENaus - Im Marketplace stehen von der Community gepflegte Actions sehr unterschiedlicher Qualität nebeneinander, die meist aus Shell-Skripten und Dockerfiles bestehen
- Das Dependency-Management ist intransparent, und unsicherer Code kann ausgeführt werden
- Wenn über die Syntax
- Beschränkungen der Compute-Umgebung
- Die Standard-Runner von GitHub Actions sind gemeinsam genutzte Runner im Besitz von Microsoft, langsam, ressourcenbeschränkt und ohne nennenswerte Anpassbarkeit
- Die Kosten größerer Runner liegen auf einem Niveau, bei dem die Finanzabteilung schon mit „Wir sollten mal reden“-Meetinganfragen reagiert, und trotzdem bleibt die Umgebung unkontrollierbar
- Es gibt mindestens sechs Startups wie Namespace, Blacksmith, Actuated, Runs-on, BuildJet und weitere, die sich ausschließlich darauf spezialisieren, die Langsamkeit von GitHub-Actions-Runnern zu beheben – allein das belegt die Schwächen der Standard-Compute-Umgebung
- Mit einem self-hosted runner lässt sich das Compute-Problem lösen, aber YAML-Expressions, Berechtigungsmodell, Marketplace und Log-Viewer bleiben unverändert problematisch
Detaillierte, aber kumulative Probleme
actions/cache: Cache-Keys sind verwirrend, Cache-Misses passieren stillschweigend, Cache-Eviction ist intransparent, sodass das Debugging des Cachings mehr Zeit kostet, als es spart- Wiederverwendbare Workflows: Lassen sich nicht über eine gewisse Tiefe hinaus verschachteln, bieten keinen sauberen Zugriff auf den Kontext des aufrufenden Workflows und können nicht isoliert getestet werden
- Berechtigungsmodell von
GITHUB_TOKEN:permissions: write-allist viel zu weitreichend, und fein granular gesetzte Berechtigungen werden durch die Interaktion zwischen Einstellungen auf Repository-, Workflow- und Job-Ebene labyrinthartig komplex - Steuerung der Parallelität (concurrency): Das Abbrechen laufender Ausführungen auf demselben Branch ist mit einer Zeile möglich, aber feinere Steuerung darüber hinaus wird nicht unterstützt
- Secrets können nicht in
if-Bedingungen verwendet werden: Bedingte Ausführung wieif: secrets.DEPLOY_KEY != ''ist nicht möglich; sicherheitstechnisch nachvollziehbar, aber für Workflows, die sowohl in Forks als auch im Haupt-Repository funktionieren müssen, sind Workarounds nötig
Die Falle von „Dann nehmen wir einfach Bash-Skripte“
- Entwickler, die von CI-YAML genervt sind, verspüren oft die Versuchung, alles durch
run:-Bash-Skripte zu ersetzen, aber mit der Zeit kommen Bedingungen, Funktionen, Argument-Parsing und Parallelisierung dazu - Drei Monate später hat man 800 Zeilen Bash, die Job-Parallelisierung mit
waitund PID-Dateien nachbaut, inklusive eigener Retry-Logik und eigener Output-Parsing-Logik - Am Ende ist man dem CI-System nicht entkommen, sondern hat sich ein noch schlechteres CI-System in Bash gebaut, ohne Test-Framework und ohne dass irgendjemand ihm folgen kann
- Bash eignet sich als Glue, aber wenn man es als Build-System oder Test-Harness benutzt, verlagert man Komplexität nur von einem Bereich mit Leitplanken in einen ohne
Der alternative Ansatz von Buildkite
-
Stabiler Log-Viewer
- Der Log-Viewer von Buildkite zeigt Logs korrekt an, ohne den Browser abstürzen zu lassen, und rendert ANSI-Farben sowie Formatierung von Test-Frameworks unverändert
- Mit der Funktion Annotation können Build-Steps Zusammenfassungen von Testfehlern, Coverage-Reports, Deployment-Links usw. direkt als Markdown auf der Build-Seite ausgeben
- Da Agents auf der eigenen Infrastruktur laufen, kann man sich per SSH auf die Build-Maschine verbinden und direkt debuggen
-
Einfache YAML-Struktur
- Das YAML von Buildkite ist eine reine Datenstruktur zur Beschreibung der Pipeline, in der nur Steps, Commands und Plugins deklariert werden
- Wenn echte Logik nötig ist, schreibt man Skripte in einer echten Programmiersprache, die lokal ausführbar ist
- Damit bleibt die Grenze „Orchestrierung in der Konfiguration, Logik im Code“ klar erhalten – genau die Grenze, die GitHub Actions verwischt
-
Volle Kontrolle über die Compute-Umgebung
- Buildkite-Agents laufen als einzelnes Binary in der eigenen Cloud, On-Premises oder auf beliebiger Custom-Hardware
- Instanztyp, Caching, lokaler Storage und Netzwerk lassen sich vollständig kontrollieren; unterstützt wird alles von großen EC2-Instanzen mit NVMe-Laufwerken und 20-GB-Docker-Layer-Cache bis hin zum Raspberry Pi
- Es gibt keine Third-Party-Industrie nach dem Motto „Buildkite, aber schneller“ – man nimmt einfach eine größere Maschine
- Für individuelle Maintainer kleiner Open-Source-Bibliotheken bleibt die kostenlose Stufe für öffentliche Repositories von GitHub Actions weiterhin wertvoll
- Die Hauptzielgruppe dieses Artikels sind Teams, die Produktionssysteme betreiben, in denen CI-Zeit als verlorene Engineering-Zeit pro Woche messbar ist und 45-minütige Builds sowohl Compute-Kosten als auch Personalkosten verursachen
- In solchen Umgebungen amortisiert sich der Overhead für den Betrieb von Buildkite-Agents sehr schnell
-
Unterstützung für dynamische Pipelines
- In Buildkite sind Pipeline-Steps Daten, und Skripte können zur Laufzeit dynamisch weitere Steps erzeugen (emit) und hochladen
- In Monorepos lassen sich so nur genau die benötigten Build- und Test-Steps auf Basis geänderter Dateien erzeugen, ohne hart codierte Matrizen oder
if: contains(...)-Spaghetti matrix,if-Bedingungen und wiederverwendbare Workflows in GitHub Actions versuchen das anzunähern, führen aber dazu, dass man eine Rube-Goldberg-Maschine in einer deklarativen Sprache mit zu geringer Ausdruckskraft baut
-
Einfachheit der Plugin-Struktur
- Strukturell ähnelt das dem GitHub-Actions-Marketplace, da ebenfalls Code aus Third-Party-Repositories eingebunden wird
- Der Unterschied ist, dass Buildkite-Plugins meistens keine Docker-Images, sondern schlanke Shell-Hooks (thin shell hooks) sind; die Oberfläche ist kleiner, und man kann den gesamten Code in wenigen Minuten lesen
- Da sie auf der eigenen Infrastruktur laufen, kann der Benutzer den blast radius selbst kontrollieren
-
Detailfunktionen mit Fokus auf User Experience
- Buildkite kann Custom Emojis (
:parrot:,:docker:usw.) neben Pipeline-Steps anzeigen; das wirkt trivial, zeigt aber sorgfältige Aufmerksamkeit für die Produkterfahrung - GitHub Actions wirkt wie ein von einem Komitee entworfenes Produkt, das sich nie gefragt hat: „Macht das eigentlich Spaß?“
- Buildkite kann Custom Emojis (
Fazit: Kriterien für die Wahl eines CI-Systems
- GitHub Actions hat den Markt durch den Vorteil der Standardintegration (default) dominiert: kostenlose Nutzung für öffentliche Repositories, eingebaut in eine Plattform, die ohnehin alle verwenden, und auf dem Niveau von „gut genug“
- Es ist das Internet Explorer unter den CI-Systemen; weil Wechselkosten real sind und Zeit begrenzt ist, wird es weiter genutzt
- Buildkite ist bei langfristiger Nutzbarkeit und Developer Experience überlegen
- Für einfache Open-Source-Projekte reicht GitHub Actions aus, aber in großen Produktionsumgebungen ist Buildkite besser geeignet
- In der Geschichte von CI-Systemen gewinnt Marktanteile nicht das beste System, sondern das System, mit dem man am einfachsten anfangen kann
- GitHub Actions ist das CI, mit dem man am leichtesten startet, Buildkite das CI, mit dem man am besten weitermacht – und langfristig ist Letzteres entscheidend
- Wenn ein CI-Tool strukturell die Zeit von Entwicklern verbraucht, liegt das Problem nicht bei den Entwicklern, sondern beim Tool selbst
Noch keine Kommentare.