Dans le cadre de notre série en cours DevOps pour la victoire, nous documentons notre parcours de transformation DevOps. Dans le chapitre 1, nous avons présenté le Triangle DevOps : Outils, architecture et personnel dans le développement de produits, où nous avons abordé les éléments fondamentaux qui favorisent le succès du DevOps. Dans le chapitre 2, nous avons exploré Les premières étapes de notre transformation DevOps, en expliquant comment nous avons jeté les bases de changements significatifs.
À présent, au chapitre 3, nous abordons le prochain défi majeur que nous avons rencontré : la conception axée sur la testabilité. Après avoir amélioré la structure de notre équipe, affiné notre définition de « terminé » et mis en place une analyse de code automatisée, nous avons réalisé que le principal goulet d'étranglement de notre pipeline s'était déplacé vers la conception logicielle, plus précisément la testabilité. Le travail s'accumulait dans la phase de test, ce qui nous empêchait d'accélérer nos cycles de publication. Ce chapitre examine en détail la manière dont nous avons surmonté ce défi et amélioré notre capacité à livrer efficacement des logiciels de haute qualité.
Le goulet d'étranglement des tests
La principale raison de ce retard résidait dans le fait que les tests étaient principalement manuels, axés sur les tests d'interface utilisateur (UI) et les flux de travail des utilisateurs. L'interface utilisateur étant généralement l'un des derniers éléments à être achevés, il n'y avait pratiquement aucune automatisation en place, et les tests manuels constituaient la seule approche viable. Les cas de test d'interface utilisateur automatisés (par exemple, les tests Selenium) étaient généralement rédigés après la fin des tests manuels, principalement à des fins de non-régression.
Cette dépendance à l'égard des tests manuels a entraîné :
- Des retards importants dans les lancements de logiciels.
- La nécessité de multiples déploiements pour permettre des tests parallèles par plusieurs membres de l'équipe QA.
Cela a mis en évidence l'importance de rendre le code plus testable afin d'optimiser la circulation de la valeur. Cependant, y parvenir s'est avéré plus difficile que prévu.
Donner la priorité aux tests d'API et aux tests unitaires
Pour relever ce défi, nous nous sommes concentrés sur deux axes principaux :
- Tests unitaires - Garantir que les composants individuels peuvent être testés de manière isolée.
- Tests d'API - Réduire la dépendance aux tests basés sur l'interface utilisateur et aux bases de données.
Tests d'API : anticiper les tests pour obtenir des retours plus rapides
Nous avons redéfini notre stratégie de test d'API grâce à ces améliorations clés :
- API bien définies - Les API ont été conçues pour être simples, bien documentées et disponibles dès le début du développement.
- Éviter la base de données comme point d'intégration - L'utilisation de bases de données pour l'intégration créait des dépendances qui ralentissaient les tests. Nous avons plutôt privilégié l'intégration basée sur les API en utilisant REST sur HTTP et GraphQL. Cela a réduit le temps de configuration de la base de données et amélioré l'automatisation des tests.
Ce changement a considérablement réduit les retards, permettant une automatisation plus rapide et des tests en amont, ce qui montre que l'accent mis sur les conceptions axées sur les API a amélioré à la fois la testabilité et l'efficacité.
Tests unitaires : un changement d'état d'esprit
Au départ, nos efforts en matière de tests unitaires ont entraîné une augmentation rapide de la couverture des tests, mais nous avons rapidement identifié des problèmes :
- Certains tests étaient superficiels, rédigés uniquement pour atteindre les objectifs de couverture de code.
- Ils manquaient d'une validation significative de la fonctionnalité, des cas limites et des scénarios d'échec.
Pour contrer cela, nous avons insisté sur :
- La formation des développeurs à l'écriture de tests utiles.
- Refactorisé le code pour améliorer la testabilité.
Difficultés liées à l'écriture de code testable
Le principal obstacle à l'efficacité des tests unitaires était l'absence de testabilité du code source lui-même. Les principaux problèmes étaient les suivants :
- Fonctions vastes et monolithiques.
- Composants fortement couplés.
- Mauvaise séparation des préoccupations.
- Abstraction insuffisante.
Ces problèmes rendaient difficile l'isolation et le test efficace de chaque unité. Pour y remédier, nous avons :
- Fourni des lignes directrices : Nous avons partagé les meilleures pratiques concernant :
- Sélectionné les fonctions destinées aux tests unitaires.
- Refactorisé le code pour améliorer la testabilité.
- Conçu et utilisé des simulations (mocks) pour faciliter les tests.
- Accent mis sur les améliorations progressives : Les développeurs ont été encouragés à apporter de petits changements significatifs pour améliorer la testabilité au fil du temps.
Des progrès lents mais réguliers
Malgré ces efforts, la progression a été lente, en particulier pour les produits hérités. L'architecture existante limitait le nombre de tests unitaires que nous pouvions écrire. Cependant, ces actions ne visaient pas seulement à améliorer la base de code actuelle ; elles constituaient un investissement pour l'avenir :
- L'amélioration des compétences des développeurs en matière de conception de code testable a garanti que les futurs projets ne connaîtraient pas les mêmes problèmes.
- Les améliorations progressives ont permis d'éviter les perturbations tout en augmentant régulièrement la couverture des tests automatisés.
- Un changement d'état d'esprit a aidé les équipes à considérer la testabilité non pas comme une contrainte, mais comme une nécessité pour la réussite durable du DevOps.
Pour les équipes et les organisations qui entament une transformation DevOps, la testabilité est un domaine d'intervention essentiel. Elle permet d'atténuer les goulets d'étranglement lors de la phase de développement. Cependant, il est important de gérer les attentes : des résultats immédiats ne sont peut-être pas possibles, en particulier face à de vastes bases de code existantes. Refactoriser un tel code sans disposer de tests unitaires et de régression suffisants représente un défi majeur.
Leçons tirées de l'ouvrage The Pragmatic Programmer
Dans les situations où il n'est pas envisageable de repartir d'une nouvelle base de code, The Pragmatic Programmer: Your Journey to Mastery, rédigé par Andy Hunt et David Thomas, propose des conseils pratiques :
- Cibler des modifications progressives : se concentrer sur la refactorisation de parties du code petites et faciles à gérer.
- Dissocier les composants : Réduire les dépendances pour faciliter les tests de chaque unité.
- Décomposer les fonctions monolithiques : Diviser les grandes fonctions en unités plus petites et plus ciblées.
- Adopter une conception modulaire : Rendre le code plus facile à tester et à maintenir en améliorant sa modularité.
Le message est clair : une fois les goulots d'étranglement initiaux résolus, la testabilité doit devenir une priorité. Cela permettra d'alléger les contraintes lors de la phase de test et d'accélérer la livraison d'un logiciel de haute qualité.
Perspectives d'avenir
Notre parcours pour améliorer la testabilité nous a enseigné de précieux enseignements sur le rôle de la conception logicielle pour faciliter des transformations DevOps fluides. En nous concentrant sur la testabilité, nous avons éliminé les goulets d'étranglement, amélioré les compétences des développeurs et posé les bases de notre réussite future. Bien que les résultats immédiats puissent être lents, les avantages à long terme en termes de qualité, d'efficacité et de croissance de l'équipe valent largement cet investissement.
Alors que nous poursuivons notre parcours DevOps, mesurer le succès devient notre prochain défi clé. Dans le chapitre 4, nous verrons comment nous avons mis en place des indicateurs clés de performance et des tableaux de bord pour suivre nos progrès et identifier de nouveaux axes d'amélioration.
Restez à l'écoute pour découvrir comment la prise de décision fondée sur les données nous a aidés à affiner nos processus DevOps et à optimiser nos performances !
