Un moyen simple d'améliorer la qualité de publication dans l'environnement RAD


15

Un peu de contexte ici - nous sommes une petite équipe (de 5) de développeurs RAD responsables du développement logiciel interne dans une grande entreprise non logicielle. Les "logiciels internes" varient d'une application .NET de bureau utilisant un serveur MSSQL comme backend à des scripts Python s'exécutant en arrière-plan de documents et de modèles MS Word - un zoo de technologies.

Toute l'équipe est composée de personnes polyvalentes capables d'obtenir les exigences des utilisateurs, de les coder, de les tester et de les déployer en production. Une fois le logiciel en production il est pris en charge par une autre équipe mais il nous est généralement facile d'intervenir en cas de problème.

Jusqu'à présent, tout sonne bien, mais il y a un problème - étant une équipe RAD, nous devons publier souvent, et il n'y a pas de jour sans que nous publions de nouvelles versions d'une ou deux applications (ou ce pourrait être un script, un document Word mis à jour) , Application console C ++, etc.) dans la production. Nous faisons un test de développement et impliquons également les utilisateurs finaux en les laissant exécuter le logiciel dans l'environnement UAT ...

... mais les bogues se faufilent quand même en production. Les utilisateurs comprennent que ces bogues et l'instabilité occasionnelle sont le prix qu'ils paient pour obtenir ce qu'ils veulent vraiment rapidement, mais en même temps, cela nous a fait réfléchir - peut-être pourrions-nous améliorer notre développement ou une pratique de publication pour améliorer la stabilité de la logiciel et réduire le nombre de bogues que nous introduisons lors de l'ajout d'une nouvelle fonctionnalité.

La bonne chose - nous n'avons pas vraiment beaucoup de processus en premier lieu, donc il devrait être facile de commencer à s'améliorer, la mauvaise chose - étant une petite équipe RAD, nous n'avons pas vraiment beaucoup de temps et de ressources pour mettre en œuvre quelque chose de grand, mais nous avons pensé aux initiatives suivantes et nous serions heureux de recevoir vos commentaires, conseils, astuces et suggestions.

  1. Actuellement, certaines applications sont mises en production juste après les tests du développeur, contournant les tests d'acceptation des utilisateurs. Cette pratique doit être abandonnée et même un petit changement doit être testé par un utilisateur final. Chaque application aura un bêta-testeur dédié sélectionné parmi les utilisateurs finaux. Ce n'est qu'après qu'un bêta-testeur a approuvé la nouvelle version qu'il est promu du test à l'environnement de production.

  2. Nous ne procédons pas à des révisions de code - mais nous commencerons à effectuer des révisions de code avant que l'un de nous ne vérifie l'ensemble de modifications. Je pensais également à un "examen du déploiement" - en gros, l'un des développeurs doit s'asseoir à côté de l'autre le regarder faire le déploiement du logiciel (copier les binaires, mettre à jour les configurations, ajouter une nouvelle table à la base de données, etc.) - il ne s'agit généralement que de prend 5 à 10 minutes, ce qui ne prendra pas beaucoup de temps pour un "examen de déploiement".

  3. Comment réduire le temps de restauration lorsqu'une nouvelle version est suffisamment buggée pour être retirée de la production et remplacée par une bonne version précédente. Nous stockons un historique de toutes les versions (sous forme de fichiers binaires) pour faciliter le retour d'une version - et bien qu'il soit rapide de "remplacer les fichiers binaires nouvellement publiés par les versions binaires précédentes", il s'agit toujours d'un processus manuel qui est sujet aux erreurs. et exigeant parfois "et si la restauration échouait et rendrait le système inutilisable au lieu d'un buggy".

C'est là que nous avons manqué de nos idées et nous aimerions avoir vos commentaires à ce sujet et si vous pouviez partager quelques conseils simples d'amélioration des processus de publication / développement - ce serait génial.


les tests unitaires automatisés et les CI semblent être exactement le genre de choses dont vous avez besoin.
Raynos

Réponses:


14

+1 pour avoir abordé un grand sujet. Lorsque nous développons la ligne de développement "Libérer les versions anticipées souvent", les choses s'accélèrent et, à mesure que l'élan s'accumule, de nombreux problèmes de ce type surviennent (comme vous l'avez décrit) que nous ne sommes par ailleurs pas très prêts à affronter. La pire crainte, c'est quand les gens voient la vitesse comme un ennemi du bon travail.

J'ai vu une littérature très limitée à ce sujet cependant, c'est ce que nous pratiquons qui aide certainement:

1. Suivi efficace des bogues
Rendre le suivi des bogues plus efficace - ce que nous faisons est non seulement de garder une liste de bogues et de cocher, mais lorsqu'il est fermé, nous devons définir certaines choses comme "les problèmes étaient-ils reproductibles?", "Est-ce une solution permanente ou un correctif? "," quelle est la cause profonde "des problèmes? Cela permet de savoir ce qui s'est passé, lorsque ce bogue était visible la dernière fois. Ceci est essentiel pour garantir que les bogues ne se répètent pas souvent.

2. Définir les principaux points de repli
Nous savons tous que des bogues arriveront, nous devrons donc fournir un repli efficace qui fonctionne le plus souvent. Maintes et maintes fois, nous finalisons (avec un ratio d'environ 1 sur 10 dans notre cas) une version la plus courante qui fonctionne partout de la manière la plus fiable. Le nombre total de versions peut être élevé, mais si quelque chose se passe mal, les solutions de repli sont rares et vous n'avez pas besoin de reculer davantage. L'un des moyens les plus simples de connaître la meilleure solution de rechange est de voir quelle version la plus ancienne a été la plus longue en production sans trop de problèmes.

3. Distinguer les versions corrigées de petits bogues risqués et stables.
Lorsque nous savons que nous avons un changement majeur d'algorithme, il est plus probable que des bogues se glissent dans des scénarios qui ne sont pas tous prévus. Où car il y a des moments où les problèmes sont très petits (ou bien compris) ainsi que peu de risques. Ne regroupez PAS de telles fonctionnalités et de simples bogues dans les mêmes versions. Ayez toujours un petit bug corrigé en premier, qui doit aller partout où vous en avez besoin. Faites des versions dédiées pour des ensembles de fonctionnalités spéciales au mieux, vous pouvez déprécier cette fonctionnalité, mais tous les autres bugs importants sont toujours disponibles corrigés dans la version précédente.

4. Branche pour le développement de fonctionnalités importantes
Tout ce qui associe des changements ayant un impact sur la conception doit être fait séparément sur une branche. Un développement plus important ne se termine pas rapidement par rapport à des bogues plus petits. Si nous introduisons des validations intermédiaires où le travail «partiel» lié à une fonctionnalité qui n'est toujours pas utilisée - est une région potentielle d'introduction de bogues; les bogues qui ne se seraient pas posés si le travail complet de la fonctionnalité s'était terminé atomiquement - d'où ce sont des bogues que nous n'aurions pas à résoudre s'il y avait des branches.

5. Planifiez toujours les versions qui sont basées sur le thème
Plusieurs fois, de nombreux bogues différents arrivent de versions différentes - mais il est préférable d'organiser les bogues (et les fonctionnalités) qui affectent des modules similaires facilitent le travail de répétition et minimisent le nombre de bogues provenant de ce travail. Préparez toujours la feuille de route de sortie bien à l'avance; les bogues continuent d'affluer - et cela tombe dans différentes versions cibles pour avoir de manière optimale un bon groupe de bogues à éliminer ensemble dans une bonne version. Lorsque des bogues similaires sont combinés, cela donne toujours une meilleure idée des scénarios contradictoires.

6. Étendez d'abord toute nouvelle version à quelques clients.
Dans notre cas, nous voyons d'abord la tester sur quelques sites et tous les autres sites ne reçoivent une version que lorsqu'il y a une demande. Plusieurs fois, certains (ou la plupart) des utilisateurs ne passeraient que d'une version stable à une autre version stable uniquement.

7. Test de régression Le
long des lignes de bogues collectés - construire une combinaison de régression. Aussi, si possible, marquez les bogues critiques et testez comme étant les plus importants qui deviennent des critères de qualification minimum à tester avant qu'une version candidate ne devienne une version.

8. Pause et réflexion
Lorsque beaucoup de choses avancent à plein régime, il devrait y avoir du temps pour faire des pauses - faites une pause et obtenez des sorties qui ne fonctionnent pas mieux. En fait, ont des vacances de sorties depuis un certain temps. (la durée est inversement proportionnelle à la fréquence). Par exemple, nous avons souvent ces versions dites de «nettoyage» qui n'apportent rien de nouveau du point de vue des fonctionnalités - mais qui aident à maintenir le code maintenable. La plupart de ces versions sont d'excellents points de repli dont vous ne vous souvenez presque jamais de l'histoire antérieure à cela.

9. Peut-être le plus étrange que
je trouve celui-ci difficile à mettre en œuvre souvent, mais c'est une bonne astuce. Échangez le propriétaire de certains modules. Lorsqu'on demande aux gens de réviser le code, il n'y a pas grand-chose à tirer de cette pratique. Mais lorsque vous devez sérieusement faire face à ce nouveau code, lorsque vous échangez des auteurs, les "mauvaises" affections potentielles sont remarquées rapidement bien avant qu'elles ne commencent à polluer le code. Bien sûr, cela réduit la vitesse - mais si vous le faites souvent, il est probable que les gens maîtrisent différentes parties du code et apprennent tout le produit, ce qui est par ailleurs très difficile à enseigner.

10. Dernier point mais non le moindre
Apprenez à revenir souvent au tableau blanc. Plus vous repensez que si cette fonctionnalité aurait fait partie de notre conception la plus initiale, comment aurions-nous pensé à la conception à ce moment-là? Parfois, le plus grand défi avec le travail incrémentiel est simplement que nous sommes trop limités par l'ordre des fonctionnalités que nous avons construit en premier et que, souvent, nous ne pouvons pas revenir aux bases. L'astuce consiste à continuer de voir comment généraliser plutôt que de prendre en compte cette nouvelle fonctionnalité ou ce nouveau scénario. Cela nécessite que la conception reste à jour, et cela ne se produit que si nous revenons souvent à la planche à dessin. De plus, à mesure que les programmeurs de nouvelle génération se joignent à eux, ils font partie du groupe de réflexion plutôt que de simplement mettre des correctifs.

EDIT
11. Gardez une trace des solutions de contournement et de conception.
Très souvent, nous sommes sous la pression de délais pour corriger le bogue et la sortie en production. Cependant, lorsque le bogue est au niveau de la conception, un certain nombre de choses doivent changer, mais souvent les gens corrigent par des raccourcis pour respecter la date limite. C'est OK . Cependant, à mesure que plusieurs de ces solutions de contournement augmentent, le code devient fragile. Gardez une trace spéciale du nombre de lacunes de conception qui sont déjà passées. En règle générale, lorsque vous négociez les délais avec le chef de projet, il est préférable de lui faire s'engager à le livrer en raccourci pour économiser la production, mais nous aurons également calendrier et ressources pour obtenir une solution permanente.


1
Gloire. Cette réponse est bien meilleure que la plupart des didacticiels en ligne
Ubermensch

Ce sont des outils très utiles et importants pour aider les équipes «résistantes à l'agilité» à apprendre à être agiles sans nécessairement tout engager à la fois pour changer la méthodologie en place. Votre 9ème point offre effectivement une opportunité de réviser le code, sans avoir besoin d'un processus de révision formel ou de passer à la programmation par paires, mais nécessite un état d'esprit sans blâme ni fierté afin d'éviter le développement de frictions inutiles. Cependant, lors de la ramification, je suggérerais de minimiser cela à une seule branche dans le but de fusionner de nouveau dans la ligne principale dès que possible ...
S.Robins

@DipanMehta La question semblait provenir d'un nouveau venu et elle méritait une réponse qui pourrait lui donner une perspective large pour s'appuyer sur les choses existantes malgré le fait qu'elle soit trop spécifique et que votre réponse en est vraiment proche.
Ubermensch

1
... comme la gestion de plusieurs branches peut devenir très problématique à gérer au fil du temps, vous voudrez donc garder vos modifications de branche petites et adaptées pour résoudre un problème spécifique, fusionner, re-branche, etc. Un bon système de contrôle de version avec support pour les espaces de travail et qui fait la différence entre une "promotion" versionnée et une "conservation" non versionnée peut aider à éviter complètement les branchements. À mon humble avis cependant, il vaut mieux obtenir le bon processus, puis trouver des outils pour s'adapter, plutôt que de faire correspondre les processus aux outils.
S.Robins

+1 pour "il vaut mieux obtenir le bon processus, puis trouver des outils adaptés, plutôt que de faire correspondre les processus aux outils"
Ubermensch

4

Je travaille également dans une petite équipe de développement (seulement 2 d'entre nous) et nous avons rencontré des problèmes similaires que vous avez mentionnés. Le principal problème pour nous est que nous avons tous les deux tendance à travailler sur des tâches distinctes et il devenait trop courant pour nous de terminer une tâche / fonctionnalité, de la tester (testé par le développeur uniquement) et de la publier rapidement. Cela entraînait de nombreuses petites versions, les utilisateurs signalant de petits bogues qui auraient dû être facilement détectés lors des tests.

Afin d'améliorer notre processus, j'ai commencé par introduire un tableau Kanban . Le tableau était très simple au départ et ne comportait que quelques colonnes (configuration à l'aide d'un tableau blanc, de fiches et d'aimants colorés):

Carnet de commandes | À faire | Terminé

Cependant, cela a rapidement évolué pour refléter notre processus actuel:

Carnet de commandes | Développement | Dev. Test | UAT | Terminé | Libéré

Avec le tableau, nous avons une règle selon laquelle chaque tâche / fonctionnalité doit être testée par un autre développeur (ainsi que par le développeur qui a implémenté la fonctionnalité). Donc, au moment où une carte atteint la colonne `` Terminé '', elle devrait avoir été testée par au moins 2 développeurs et également testée par l'utilisateur.

Il y a beaucoup d'autres avantages à utiliser Kanban. Pour nous, cela a amélioré la communication et nous a aidés à partager nos connaissances, car nous sommes tous les deux impliqués dans une certaine mesure dans chaque tâche. Cela a également amélioré notre processus de publication, car nous pouvons maintenant voir exactement quelles tâches / fonctionnalités sont prêtes à être publiées / terminées et peuvent parfois suspendre la publication si nous savons que d'autres tâches seront bientôt effectuées. Pour les personnes extérieures à l'équipe, le tableau sert également de référence rapide pour voir quelles tâches nous avons planifiées, les travaux en cours et ce qui a été publié récemment.

En passant, nous utilisons des aimants colorés (un par développeur) pour signaler la carte sur laquelle nous travaillons actuellement, mais une autre option consiste à ajouter des couloirs de natation (rangées), un par couloir et à placer des cartes Kanban dans les couloirs de baignade appropriés. Encore une fois, cela aide comme référence rapide pour voir sur quoi travaille actuellement chaque développeur.

Autres liens que j'ai trouvés utiles:

Kanban appliqué au développement logiciel: d'Agile à Lean

Un système Kanban pour le génie logiciel - Vidéo

J'espère que Kanban abordera le point 1. de votre question. En ce qui concerne les révisions de code, nous le faisons au stade des tests de développement afin que toutes les modifications requises après la révision soient testées de nouveau avant de passer à l'UAT. La restauration dépend de votre environnement, mais nous déployons des fichiers d'application sur les serveurs Terminal Server à l'aide de fichiers batch qui renomment les fichiers actuels et les copient sur de nouveaux fichiers à partir d'un serveur central et nous pouvons restaurer assez facilement en plaçant la sauvegarde (fichiers précédents) à l'emplacement central et relance des scripts.


4

Vous avez déjà identifié que vous savez qu'il y a des problèmes avec vos processus qui affectent la qualité de votre logiciel, et bien que cette question provoquera une gamme de réponses, ma suggestion serait d'examiner le sujet du génie logiciel et d'essayer de savoir les développeurs se retrouvent de plus en plus à faire dans ce domaine. Je vous suggère de commencer à lire quelques bonnes ressources pour vous lancer. Quelques-uns qui me viennent à l'esprit:

  • Lean Software Development par Mary et Tom Poppendeick fournit une excellente lecture pour les personnes intéressées à apprendre à identifier les «déchets» et à faire pour changer les processus afin de devenir plus légers et plus efficaces.
  • Head First Software Development par Dan Pilone et Russ Miles est un peu comme un de ces livres "pour les nuls" à première vue, mais en regardant un peu au-delà du style de présentation chaotique, il contient la plupart des informations relatives aux bases du génie logiciel et a un bref article sur le développement piloté par les tests.
  • Présentation de BDD est la page de Dan North sur l'accès au développement piloté par le comportement, ou peut-être préféreriez-vous un wiki BDD . Ce sont des références de démarrage pour BDD et vous voudrez probablement examiner des outils et des cadres pour vous aider. La chose importante à comprendre est que le BDD est effectivement TDD porté à un niveau conceptuel supérieur. Il vous permet de penser aux tests comme vous pensez aux spécifications et de tester dans la même langue que vous utilisez lorsque vous écrivez des spécifications. Les frameworks s'intègrent généralement à d'autres frameworks de tests unitaires, vous obtenez donc le meilleur des deux mondes si vous décidez que vos tests ne bénéficieront pas nécessairement de la syntaxe BDD.
  • L'article Agile sur le développement de logiciels de Wikipedia est une bonne introduction au développement de logiciels agiles et fournit un certain nombre de références utiles et de liens vers des articles par certaines des personnes les plus respectées de la communauté du développement.

Afin d'améliorer COMMENT vous travaillez, vous devez vous permettre d'être complètement ouvert et disposé à sortir bien de votre zone de confort afin d'apprendre à améliorer les choses que vous faites sans vous accrocher à certains concepts que vous pourriez trouver sont plus confortable à accrocher. Parlant d'une expérience personnelle, c'est probablement la chose la plus difficile à faire ou à encourager chez les autres.

Le changement est difficile dans le meilleur des cas, même si vous sentez que vous recherchez activement le changement, donc le meilleur conseil que je puisse vraiment vous donner est de regarder les différentes méthodologies Agiles qui ont été développées au fil des ans pour vous familiariser avec les pratiques qui sont considérées comme les plus importantes (par exemple: tests unitaires, intégration continue, refactoring, etc ...), puis choisissez la méthodologie qui semble la plus proche de ce avec quoi vous et votre équipe vous sentirez le plus à l'aise. Une fois que vous avez pris votre décision, ajustez les pratiques et votre processus de développement afin qu'il corresponde à la façon dont votre équipe préférerait travailler, en gardant à l'esprit ces principes lean et comment vous souhaitez travailler afin que votre équipe puisse produire la plus grande valeur avec le moins de déchets. Finalement,

Si vous pensez que vos processus ont simplement besoin d'être peaufinés, mais que vous craignez que votre chaîne d'outils ne réponde pas tout à fait à vos besoins, alors envisagez peut-être des améliorations. À tout le moins, un produit d'intégration à intégration continue (tel que Continuum, Cruise Control ou Hudson) et un système de suivi des problèmes (comme Jira ou Redmine) devraient être une priorité pour vous aider à automatiser certains de vos processus de génération et de publication, et pour garder vos bogues et demandes de fonctionnalités sous contrôle.

La réalité est que, quel que soit le niveau de vos processus RAD, si vous ne consacrez pas de temps à faire en sorte que les choses soient «correctes» pour votre équipe, vos problèmes ne feront que croître avec le temps et votre perception du temps disponible rétrécir en conséquence. Les grands changements sont généralement hors de question sous une forte pression temporelle, mais essayez de vous donner un peu de "marge de manœuvre" afin de mettre en place des systèmes pour vous aider à faire des pas de bébé vers la vision de votre équipe d'une méthodologie idéale.


Je faisais référence à notre équipe en tant qu'équipe de développeurs "RAD" pour souligner le fait que nous travaillons dans le "développement rapide d'applications" où les cycles de développement sont extrêmement courts. Il n'y a donc rien à voir avec les outils RAD ou les IDE. Merci pour votre réponse.
PeterT

@PeterT: Ah! Mes excuses pour ce malentendu. J'ai dû effleurer votre 3e paragraphe et manquer le contexte. Je vais modifier ma réponse en fonction, mais les conseils dans l'ensemble restent toujours dans leur contexte. :-)
S.Robins

2

Chaque fois que j'entends parler de défauts, mes premières questions portent sur leur origine et leur détection et leur élimination. L'efficacité de l'élimination des défauts est un bon moyen de mesurer et de suivre cela. En sachant d'où proviennent les défauts et en travaillant à améliorer les processus à ces phases, vous pouvez réduire le temps et le coût d'un projet. Il est bien connu qu'il est moins coûteux de réparer les défauts plus près de leur point d'injection, donc une fois que vous savez d'où viennent les défauts, vous pouvez ensuite regarder les changements d'activités pour améliorer ces phases.

Une fois que vous avez des informations sur l'origine des défauts, vous pouvez voir exactement quelles techniques et technologies vous souhaitez appliquer. Les révisions des exigences, de la conception et du code, les tests automatisés, l'analyse statique, l'intégration continue et les tests plus poussés menés par les utilisateurs peuvent être des options que vous devriez examiner, selon les phases qui génèrent des défauts.

Pour développer votre désir de révisions de code, vous devriez également considérer différents niveaux de révisions de code en fonction de la priorité et du risque d'un module. Les modules à faible risque et à faible priorité pourraient ne pas avoir besoin du tout d'un examen du code, ou peut-être simplement d'une simple vérification sur place, où un autre développeur lit simplement le code par lui-même et fournit des commentaires, pourrait fonctionner. D'autres techniques de révision de code incluent la programmation de paires, des procédures pas à pas, des critiques et des inspections avec différents nombres de développeurs.

Aux fins de restauration, je chercherais à automatiser ce processus en utilisant une sorte de scripts pour le rendre plus rapide et moins sujet aux erreurs. Dans un monde parfait, je voudrais augmenter la qualité des produits expédiés de sorte qu'il ne soit pas nécessaire de revenir en arrière, et vous pouvez y parvenir. Avoir la capacité, cependant, pourrait être une bonne idée, mais la rendre aussi indolore que possible.


1

Comme d'autres l'ont souligné, l'ajout de tests de régression aidera à éviter que les mêmes défauts n'apparaissent à l'avenir. Cependant, si vous rencontrez de nouveaux défauts, il peut être temps d' ajouter des assertions (aka contrats) au code qui spécifient les pré-conditions, les post-conditions et les invariants des classes et méthodes.

Par exemple, si vous avez une classe où une méthode ne peut accepter que des nombres entre 10 et 25 (c'est ce qu'on appelle la condition préalable), vous ajouteriez une instruction assert au début de la méthode. Lorsque cette assertion échoue, le programme se bloque immédiatement et vous pourrez suivre la chaîne de méthodes qui a conduit à cet échec.

Python, PHP et d'autres langages de programmation sont typés dynamiquement et n'ajoutent pas beaucoup de conditions aux méthodes. Tout ce qui est nécessaire pour que quelque chose fonctionne, c'est qu'il implémente une méthode particulière. Je soupçonne que vous avez besoin de plus de conditions sur vos méthodes. Vous devez définir et tester pour vous assurer qu'une méthode peut réellement fonctionner dans son environnement.

Pour les programmes C / C ++, j'ai trouvé que l'ajout d'assertions pour tester l'allocation de mémoire était très utile pour réduire le nombre de fuites de mémoire dans le programme.


Eh bien, je conviens que la vérification des assertions / post / conditions préalables est une bonne pratique de programmation et sera finalement payante, mais ma question visait à améliorer la qualité des versions très fréquentes, pas la qualité du code en général.
PeterT

Cela sera payant tout de suite car vous devrez commencer par ajouter des assertions / vérification de condition dans chaque version pour les nouvelles fonctionnalités / corrections de bugs. Ce serait une tâche énorme d'ajouter des assertions à l'ensemble du projet en une seule fois; p
Rudolf Olah

Il y a cependant un truc avec les assertions - et si on se trompait. Que faire si nous pensons que la méthode ne doit accepter que des nombres compris entre 10 et 25, mais en réalité, il est acceptable d'élargir la plage à [0; 50] et cela n'a été trouvé qu'après qu'une nouvelle version a été déployée et en production depuis un journée. Si une méthode en cours est une méthode de bas niveau et utilisée dans de nombreux endroits, nous ne pouvons pas faire grand-chose, mais rééditer avec un correctif. Cependant, si nous n'avions pas ajouté l'assertion au niveau de la méthode pour utiliser un bloc try-catch de niveau supérieur, nous pourrions nous en tirer avec seulement une partie des fonctionnalités ....
PeterT

... non disponible afin que nous puissions nous acheter un peu de temps pour faire un "bon" ou l'appeler "une sortie programmée" une semaine plus tard. Je pense que vous voyez mon point. Merci pour votre commentaire.
PeterT
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.