Im zweiten Kapitel dieser Reihe – DevOps für den Erfolg – beleuchten wir die entscheidenden ersten Schritte einer DevOps-Transformationsreise. Die ersten Schritte auf jeder Transformationsreise sind oft die schwierigsten. Bei DevOps ist es entscheidend, die richtigen ersten Schritte zu identifizieren – solche, die sowohl wirkungsvoll als auch umsetzbar sind –, um bedeutsame Fortschritte zu erzielen.
Viele Organisationen machen den Fehler, Best Practices blind zu implementieren, ohne ihre einzigartigen Einschränkungen zu berücksichtigen, was oft zu Frustration und einer Ermüdung durch Misserfolge führt. Stattdessen ist ein strategischerer Ansatz erforderlich – einer, der Engpässe identifiziert und systematisch beseitigt.
Wie identifiziert man die ersten Schritte?
Ein wirkungsvolles Prinzip zur Identifizierung des Startpunkts stammt aus einer vor 30 Jahren veröffentlichten Studie in Das Ziel: Ein Prozess der kontinuierlichen Verbesserung von Eliyahu M. Goldratt. Die Idee ist einfach: Identifizieren Sie den primären Engpass – den limitierenden Faktor, der die Systemleistung einschränkt. In unserem Fall wurde dies deutlich, als sich die Arbeit beim Entwicklungsteam stapelte. Aufgaben wurden begonnen, aber nicht abgeschlossen, und der Rückstand wuchs stetig.
Um die Ursachen aufzudecken, organisierten wir Deep-Dive-Sitzungen mit Entwicklungsteams, Tech Leads und Produktmanagern. Diese Diskussionen brachten mehrere zentrale Probleme ans Licht:
- Teams übernahmen neue Aufgaben, bevor sie bestehende abgeschlossen hatten.
- Es gab keine klaren Kriterien für den Abschluss von Aufgaben – was Entwickler als „erledigt“ betrachteten, wich oft von den Erwartungen der Stakeholder ab.
- Es gab keine objektiven Qualitätsmessungen, bevor die Arbeit als abgeschlossen markiert wurde, was zu Mängeln und Nacharbeit führte.
- Häufige Neuzuweisungen störten die Teamstabilität und führten zu Ineffizienzen.
Etablierung stabiler Teams
Eine der ersten Korrekturmaßnahmen war die Umstrukturierung der Teams für mehr Konsistenz und Fokus. Anstatt Gruppen von Einzelpersonen als Ad-hoc-Arbeitsgruppen zu behandeln, bildeten wir stabile, langfristige Teams mit klar definierten Verantwortlichkeiten.
Die Einrichtung einiger Teams, wie Entwicklungsteams, war relativ unkompliziert. Die Organisation von Support-Teams – wie Infrastruktur, Projektmanagement, Produktmanagement und Business Analysis – erwies sich jedoch als schwieriger. Wir durchliefen mehrere Iterationen, um die richtigen Kombinationen zu finden.
Gemäß dem in den Team Topologies von Matthew Skelton und Manuel Pais dargelegten Prinzip des Team-First-Denkens, haben wir:
- Kleine, autonome Teams (4–7 Mitglieder) geschaffen, um tiefgreifendes Fachwissen und Verantwortlichkeit zu fördern.
- Teamübergreifende Abhängigkeiten beseitigt, indem wir sicherstellten, dass Einzelpersonen vollständig einem einzigen Team zugeordnet waren.
- Teamstrukturen weiterentwickelt, insbesondere für unterstützende Funktionen wie Infrastruktur und Produktmanagement, um den Arbeitsfluss zu optimieren.
Einsatz von Tools für Transparenz und Management
Nachdem die Teams strukturiert waren, sind wir für das Backlog- und Arbeitsmanagement zu Azure DevOps übergegangen. Eine einheitliche Plattform ermöglichte:
- Bessere Arbeitstransparenz, damit Teams den Fortschritt transparent verfolgen konnten.
- Standardisierte Definitionen von „erledigt“, um Erwartungen funktionsübergreifend abzugleichen.
- Verbessertes Backlog-Management, wodurch eine Umfangserweiterung und Priorisierungskonflikte reduziert wurden.
Einführung objektiver Qualitätsmetriken
Um das Problem unklarer Qualitätsmessungen vor der Markierung von Aufgaben als „bereit zum Testen“ anzugehen, haben wir ein Tool zur statischen Code-Analyse integriert, das objektive Einblicke in Folgendes bietet:
- Code-Abdeckung
- Sicherheitslücken
- Code-Smells
Wir haben auch unsere Definition von „Erledigt“ erweitert, um Qualitätsprüfungen zu einem obligatorischen Schritt zu machen, bevor Arbeitselemente als abgeschlossen markiert werden.
Verwaltung von Work in Progress (WIP)
Eine der wirkungsvollsten Änderungen war die Überwachung und Begrenzung von Work in Progress (WIP). Ein hohes WIP deutete auf Engpässe hin, was uns ermöglichte, Bereiche, in denen die Arbeit ins Stocken geriet, proaktiv anzugehen.
Dieser systematische Ansatz, der in The Goal und Team Topologies verwurzelt ist, legte den Grundstein für kontinuierliche Verbesserung, indem er Abhängigkeiten reduzierte und die Verantwortlichkeit verbesserte.
Auf unserem Weg der DevOps-Transformation erkannten wir nach den anfänglichen Verbesserungen, dass die Hauptbeschränkung des Arbeitsflusses sich auf das Softwaredesign verlagerte – insbesondere auf die Testbarkeit unseres Codes. Nachdem wir anfängliche Engpässe durch die Verbesserung der Teamstruktur, die Definition einer „Definition of Done“ und die Implementierung von Code-Analysen behoben hatten, stellten wir fest, dass sich während der Testphase die Arbeit staute. Von Scrum-Teams entwickelter Code wartete oft darauf, von den QA-Mitarbeitern innerhalb derselben Scrum-Teams getestet zu werden.
Behebung des nächsten Engpasses: Die Testlücke
Nachdem die anfänglichen Engpässe behoben waren, entstand eine neue Einschränkung – Testverzögerungen. Während Entwicklungsaufgaben effizient voranschritten, wurde das Testen zu einem Hindernis, das schnellere Veröffentlichungen verhinderte. Bei der Untersuchung identifizierten wir die wichtigsten Herausforderungen, die den Prozess verlangsamten:
- Abhängigkeit von manuellem Testen: Die meisten Tests wurden manuell durchgeführt, was zu langsamen Feedback-Schleifen und einer verzögerten Fehlererkennung führte.
- UI-Abhängigkeit beim Testen: Da UI-Komponenten typischerweise zuletzt fertiggestellt wurden, konnte das Testen erst spät im Entwicklungszyklus beginnen.
- Mangel an proaktiver Automatisierung: Automatisierte Tests, wie Selenium-basierte UI-Tests, wurden erst nach dem manuellen Testen geschrieben, was ihre Wirksamkeit bei der Validierung in frühen Phasen einschränkte.
Umstellung auf testgetriebene Ansätze
Um diesen Engpass zu beseitigen, priorisierten wir Unit-Tests und API-Tests gegenüber UI-Tests. Die Umstellung erforderte grundlegende Änderungen in der Entwicklungsmentalität und im Softwaredesign:
Verbesserungen beim API-Testen
Um API-Tests effizient zu gestalten:
- Gut definierte APIs: APIs mussten einfach, gut dokumentiert und frühzeitig in der Entwicklungsphase verfügbar sein, damit Tester proaktiv Testfälle erstellen konnten.
- Vermeidung von Datenbanken als Integrationspunkt:
- Die Nutzung von Datenbanken als Integrationspunkt zwischen Teams schuf Abhängigkeiten, die die Tests verlangsamten.
- Die Erstellung von Testfällen erforderte das Einrichten von Datenbanken mit komplexen Daten, was den Einrichtungsaufwand erhöhte und die Möglichkeit einschränkte, mehrere Szenarien zu testen.
- Durch die Umstellung auf API-basierte Integration (hauptsächlich REST über HTTP und neuerdings GraphQL) konnten wir die Verzögerungen und Komplexitäten, die durch Datenbankabhängigkeiten entstanden, erheblich reduzieren.
Diese Umstellung ermöglichte schnellere Fortschritte bei der Automatisierung von Testfällen für APIs, was zeigte, dass die Konzentration auf API-First-Designs sowohl die Testbarkeit als auch die Effizienz verbesserte.
Unit-Testing-Praktiken stärken
Komponententests stellten eine größere Herausforderung dar:
- Schneller Fortschritt, dann „Coverage Theater“: Anfangs erreichten wir hohe Code-Abdeckungsraten, aber Code-Reviews zeigten, dass viele Unit-Tests oberflächlich waren und nur geschrieben wurden, um die Abdeckungsziele zu erreichen. Diese Tests prüften keine sinnvolle Funktionalität, deckten keine Fehlerszenarien ab und validierten keine Grenzfälle.
- Umdenken erforderlich: Entwickler mussten den Wert von Unit-Tests verstehen – nicht nur zur Verbesserung der Code-Qualität, sondern auch zur Beschleunigung der Entwicklung, indem Probleme frühzeitig erkannt werden.
Herausforderungen beim Schreiben von testbarem Code
Das größte Hindernis für effektives Unit-Testing war die mangelnde Testbarkeit der Codebasis selbst. Zu den Hauptproblemen gehörten:
- Große, monolithische Funktionen.
- Eng gekoppelte Komponenten.
- Schlechte Trennung von Belangen.
- Mangelnde Abgrenzung.
Diese Probleme erschwerten es, einzelne Einheiten effektiv zu isolieren und zu testen. Um dies zu beheben, haben wir:
- Bereitgestellte Richtlinien: Wir haben bewährte Verfahren zu folgenden Themen geteilt:
- Auswahl von Funktionen für das Unit-Testing.
- Den Code refaktorieren, um die Testbarkeit zu verbessern.
- Entwicklung und Einsatz von Mocks zur Erleichterung des Testens.
- Fokus auf schrittweise Verbesserungen: Entwickler wurden ermutigt, kleine, aber wirkungsvolle Änderungen vorzunehmen, um die Testbarkeit im Laufe der Zeit zu verbessern.
Fortschritte, Herausforderungen und der weitere Weg
Bei älteren Produkten blieb die Verbesserung der Testbarkeit aufgrund architektonischer Einschränkungen ein langsamer Prozess. Diese Maßnahmen zielten jedoch nicht nur darauf ab, die aktuelle Codebasis zu verbessern – sie waren eine Investition in die Zukunft:
- Entwickler entwickelten bessere Gewohnheiten, indem sie die Testbarkeit in neue Codebasen integrierten.
- Teams wurden autarker, wodurch die Abhängigkeit von externen QA-Bemühungen reduziert wurde.
- Die Organisation vermied es, frühere Fehler zu wiederholen, und stellte sicher, dass zukünftige Produkte einfacher zu warten und weiterzuentwickeln waren.
Die wichtigste Erkenntnis? Bei DevOps geht es nicht darum, Tools oder Checklisten zu implementieren. Es geht darum, den Arbeitsfluss kontinuierlich zu verbessern, eine Einschränkung nach der anderen.
Im nächsten Kapitel dieser Reihe werden wir „Design für Testbarkeit“ untersuchen. Wir werden genauer untersuchen, wie die Verbesserung des Software-Designs Testbeschränkungen beseitigen und schnellere Feedback-Schleifen sowie eine höhere Softwarequalität ermöglichen kann – zuerst in Teamstrukturen, dann im Workflow-Management und schließlich in der Testbarkeit. So legen wir den Grundstein für eine nachhaltige DevOps-Transformation.