In unserer fortlaufenden Serie "DevOps for the Win" haben wir unsere DevOps-Transformationsreise dokumentiert. In Kapitel 1 haben wir das DevOps-Dreieck: Tools, Architektur und Menschen in der Produktentwicklungvorgestellt, wo wir die grundlegenden Elemente erörtert haben, die den Erfolg von DevOps vorantreiben. In Kapitel 2 erforschten wir Die ersten Schritte in unserer DevOps-Transformationwie wir die Grundlage für sinnvolle Veränderungen geschaffen haben.
Nun, in Kapitel 3, widmen wir uns der nächsten großen Herausforderung, der wir begegnet sind: Design für Testbarkeit. Nachdem wir unsere Teamstruktur verbessert, unsere Definition of Done verfeinert und eine automatisierte Codeanalyse implementiert hatten, stellten wir fest , dass sich der primäre Engpass in unserer Pipeline auf das Software-Design verlagert hatte – genauer gesagt auf die Testbarkeit. Die Arbeit stapelte sich in der Testphase und hinderte us schnellere Release-Zyklen zu erreichen. In diesem Kapitel wird erläutert, wie wir diese Herausforderung gemeistert und unsere Fähigkeit verbessert haben, effizient hochwertige Software zu liefern.
Der Engpass beim Testen
Der Hauptgrund für diese Verzögerung war, dass die Tests überwiegend manuell durchgeführt wurden, wobei der Schwerpunkt auf UI-Tests und Benutzer-Workflows lag. Da die Benutzeroberfläche in der Regel eines der letzten Elemente war, die fertiggestellt werden mussten, gab es wenig bis gar keine Automatisierung, so dass manuelle Tests der einzig praktikable Ansatz waren. Automatisierte UI-Testfälle (z. B. Selenium-Tests) wurden in der Regel nach Abschluss der manuellen Tests geschrieben, hauptsächlich zu Regressionszwecken.
Dieser Rückgriff auf manuelle Tests führte zu -
- Erhebliche Verzögerungen bei Software-Releases.
- Die Notwendigkeit mehrerer Bereitstellungen, um parallele Tests durch mehr als ein QA zu ermöglichen.
Dies machte deutlich, wie wichtig es ist, den Code besser testbar zu machen, um den Wertefluss zu optimieren. Dies zu erreichen, erwies sich jedoch als schwieriger, als wir erwartet hatten.
Priorisierung von API-Tests und Unit-Tests
Um diese Herausforderung zu bewältigen, haben wir uns auf zwei Hauptbereiche konzentriert:
- Unit Tests - Sicherstellung, dass einzelne Komponenten isoliert getestet werden können.
- API-Tests - Verringerung der Abhängigkeit von UI-basierten Tests und Datenbankabhängigkeiten.
API-Tests: Linksverschiebung für schnelleres Feedback
Mit diesen wichtigen Verbesserungen haben wir unsere API-Teststrategie neu definiert:
- Gut definierte APIs APIs so konzipiert, dass sie einfach, gut dokumentiert und frühzeitig in der Entwicklung verfügbar sind.
- Vermeidung von Datenbanken als Integrationspunkt - Die Abhängigkeit von Datenbanken für die Integration führte zu Abhängigkeiten, die das Testen verlangsamten. Stattdessen haben wir uns für eine API-basierte Integration mit REST über HTTP und GraphQL entschieden. Dadurch wurde die Einrichtungszeit für Datenbanken minimiert und die Testautomatisierung verbessert.
Durch diese Umstellung wurden Verzögerungen erheblich reduziert und eine schnellere Automatisierung und frühzeitige Tests ermöglicht, was zeigt, dass die Konzentration auf API-First-Designs sowohl die Testbarkeit als auch die Effizienz verbessert.
Unit-Tests: Eine Änderung der Denkweise
Anfänglich führten unsere Unit-Tests zu einer raschen Erhöhung der Testabdeckung, aber wir erkannten schnell Probleme:
- Einige Tests waren oberflächlich und wurden nur geschrieben, um die Codeabdeckungsziele zu erreichen.
- Es fehlte eine aussagekräftige Validierung der Funktionalität, der Grenzfälle und der Fehlerszenarien.
Um dem entgegenzuwirken, betonten wir:
- Schulung von Entwicklern in der Erstellung wertvoller Tests.
- Refactoring von Code zur Verbesserung der Testbarkeit.
Herausforderungen beim Schreiben von testbarem Code
Das größte Hindernis für effektive Unit-Tests war der Mangel an Testbarkeit in der Codebasis selbst. Zu den wichtigsten Problemen gehörten:
- Große, monolithische Funktionen.
- Eng gekoppelte Komponenten.
- Schlechte Trennung der Anliegen.
- Unzureichende Abstraktion.
Diese Probleme machten es schwierig, einzelne Einheiten zu isolieren und effektiv zu testen. Um dieses Problem zu lösen, haben wir:
- Bereitgestellte Leitlinien: Wir haben bewährte Praktiken ausgetauscht:
- Auswahl von Funktionen für Unit-Tests.
- Refactoring von Code zur Verbesserung der Testbarkeit.
- Entwurf und Verwendung von Mocks zur Erleichterung von Tests.
- Konzentration auf inkrementelle Verbesserungen: Die Entwickler wurden ermutigt, kleine, sinnvolle Änderungen vorzunehmen, um die Testbarkeit im Laufe der Zeit zu verbessern.
Langsame, aber stetige Fortschritte
Trotz dieser Bemühungen kamen wir nur langsam voran, insbesondere bei älteren Produkten. Die bestehende Architektur begrenzte die Anzahl der Unit-Tests, die wir schreiben konnten. Diese Maßnahmen zielten jedoch nicht nur auf die Verbesserung der aktuellen Codebasis ab - sie waren eine Investition in die Zukunft:
- Die Verbesserung der Fähigkeiten der Entwickler bei der Entwicklung von testbarem Code stellte sicher, dass künftige Projekte nicht unter denselben Problemen leiden würden.
- Inkrementelle Verbesserungen verhinderten Unterbrechungen, während der Umfang der Testautomatisierung stetig zunahm.
- Ein Umdenken half den Teams, Testbarkeit nicht als Belastung, sondern als Notwendigkeit für nachhaltigen DevOps-Erfolg zu sehen.
Für Teams und Unternehmen, die eine DevOps-Transformation anstreben, ist die Testbarkeit ein wichtiger Schwerpunkt. Sie trägt dazu bei, Engpässe in der Entwicklungsphase zu beseitigen. Es ist jedoch wichtig, die Erwartungen zu steuern - unmittelbare Ergebnisse sind möglicherweise nicht möglich, insbesondere wenn es um große Legacy-Codebasen geht. Das Refactoring eines solchen Codes ohne ausreichende Unit- und Regressionstests stellt eine große Herausforderung dar.
Lektionen aus Der pragmatische Programmierer
Für Fälle, in denen es nicht möglich ist, mit einer neuen Codebasis zu beginnen, bietet The Pragmatic Programmer: Your Journey to Mastery, von Andy Hunt und David Thomas bietet eine praktische Anleitung:
- Inkrementelle Änderungen anstreben: Konzentrieren Sie sich auf das Refactoring kleiner, überschaubarer Teile der Codebasis.
- Entkoppeln Sie Komponenten: Reduzieren Sie Abhängigkeiten, um einzelne Einheiten leichter testen zu können.
- Monolithische Funktionen aufbrechen: Teilen Sie große Funktionen in kleinere, gezieltere Einheiten auf.
- Modulares Design anwenden: Erhöhen Sie die Testbarkeit und Wartungsfreundlichkeit des Codes durch verbesserte Modularität.
Die Botschaft ist klar: Sobald die anfänglichen Engpässe beseitigt sind, sollte die Testbarkeit zu einem Hauptschwerpunkt werden. Dadurch werden die Beschränkungen in der Testphase verringert und eine schnellere Bereitstellung hochwertiger Software ermöglicht.
Blick in die Zukunft
Unsere Bemühungen um eine bessere Testbarkeit haben us Erkenntnisse über die Rolle des Software-Designs für eine reibungslose DevOps-Transformation gebracht. Durch die Fokussierung auf Testbarkeit haben wir Engpässe beseitigt, die Fähigkeiten der Entwickler verbessert und die Grundlage für zukünftige Erfolge geschaffen. Auch wenn sich unmittelbare Ergebnisse nur langsam einstellen, lohnt sich diese Investition aufgrund der langfristigen Vorteile in Bezug auf Qualität, Effizienz und Teamwachstum.
Während wir unsere DevOps-Reise fortsetzen, wird die Erfolgsmessung zur nächsten zentralen Herausforderung. In Kapitel 4 werden wir untersuchen, wie wir KPIs und Dashboards eingerichtet haben, um unsere Fortschritte zu verfolgen und weitere Verbesserungsbereiche zu identifizieren.
Bleiben Sie dran, wenn wir uns damit befassen, wie datengestützte Entscheidungsfindung us dabei geholfen hat, unsere DevOps-Prozesse us und die Leistung zu optimieren!