Pour vous donner un petit aperçu: je travaille pour une entreprise avec environ douze développeurs Ruby on Rails (+/- stagiaires). Le travail à distance est courant. Notre produit est composé de deux parties: un noyau plutôt gras, et mince pour les grands projets clients construits sur lui. Les projets clients élargissent généralement le cœur. L'écrasement des fonctionnalités clés ne se produit pas. Je pourrais ajouter que le noyau a quelques parties plutôt mauvaises qui ont un besoin urgent de refactorisations. Il y a des spécifications, mais surtout pour les projets clients. La pire partie du noyau n'est pas testée (pas comme il se doit ...).
Les développeurs sont divisés en deux équipes, travaillant avec un ou deux PO pour chaque sprint. Habituellement, un projet client est strictement associé à l'une des équipes et des bons de commande.
Maintenant notre problème: assez souvent, nous nous cassons les affaires. Un membre de l'équipe A étend ou remanie la fonctionnalité principale Y, provoquant des erreurs inattendues pour l'un des projets clients de l'équipe B. Généralement, les changements ne sont pas annoncés par les équipes, donc les bugs sont presque toujours inattendus. L'équipe B, y compris le PO, a pensé que la fonctionnalité Y était stable et ne l'a pas testée avant de la relâcher, ignorant les changements.
Comment se débarrasser de ces problèmes? Quel type de «technique d'annonce» pouvez-vous me recommander?
Réponses:
Je recommanderais de lire Travailler efficacement avec le code hérité de Michael C. Feathers . Il explique que vous avez vraiment besoin de tests automatisés, comment vous pouvez facilement les ajouter, si vous ne les avez pas déjà, et quel "code sent" pour refactoriser de quelle manière.
En plus de cela, un autre problème central dans votre situation semble être un manque de communication entre les deux équipes. Quelle est la taille de ces équipes? Travaillent-ils sur différents arriérés?
C'est presque toujours une mauvaise pratique de diviser les équipes selon votre architecture. Par exemple, une équipe principale et une équipe non principale. Au lieu de cela, je créerais des équipes sur un domaine fonctionnel, mais multi-composants.
la source
C'est le problème. Une refactorisation efficace dépend fortement de la suite de tests automatisés. Si vous n'en avez pas, les problèmes que vous décrivez commencent à apparaître. Ceci est particulièrement important si vous utilisez un langage dynamique comme Ruby, où il n'y a pas de compilateur pour intercepter les erreurs de base liées au passage des paramètres aux méthodes.
la source
Les réponses précédentes qui vous indiquent de meilleurs tests unitaires sont bonnes, mais je pense qu'il pourrait y avoir des problèmes plus fondamentaux à résoudre. Vous avez besoin d'interfaces claires pour accéder au code principal à partir du code des projets clients. De cette façon, si vous refactorisez le code principal sans altérer le comportement observé par le biais des interfaces , le code de l'autre équipe ne se cassera pas. Il sera ainsi beaucoup plus facile de savoir ce qui peut être refactorisé "en toute sécurité" et ce qui nécessite une refonte, éventuellement une rupture d'interface.
la source
D'autres réponses ont mis en évidence des points importants (davantage de tests unitaires, des équipes de fonctionnalités, des interfaces propres aux composants principaux), mais il y a un point que je trouve manquant, qui est le versioning.
Si vous gelez le comportement de votre core en effectuant une version 1 et que vous placez cette version dans un système de gestion d'artefact privé 2 , tout projet client peut déclarer sa dépendance à l'égard de la version de base X , et il ne sera pas interrompu par la prochaine version X + 1 .
La "politique d'annonce" se réduit alors à avoir un fichier CHANGES avec chaque version, ou à avoir une réunion d'équipe pour annoncer toutes les fonctionnalités de chaque nouvelle version principale.
De plus, je pense que vous devez mieux définir ce qui est "noyau" et quel sous-ensemble est "clé". Vous semblez (correctement) éviter d'apporter de nombreuses modifications aux "composants clés", mais vous autorisez des modifications fréquentes du "noyau". Pour pouvoir compter sur quelque chose, vous devez le garder stable; si quelque chose n'est pas stable, ne l'appelez pas core. Peut-être pourrais-je suggérer de l'appeler "composants auxiliaires"?
EDIT : Si vous suivez les conventions du système de version sémantique , tout changement incompatible dans l'API du noyau doit être marqué par un changement de version majeur . Autrement dit, lorsque vous modifiez le comportement du noyau existant précédemment, ou supprimez quelque chose, et pas seulement ajoutez quelque chose de nouveau. Avec cette convention, les développeurs savent que la mise à jour de la version «1.1» vers «1.2» est sûre, mais passer de «1.X» à «2.0» est risqué et doit être soigneusement examiné.
1: Je pense que cela s'appelle une gemme, dans le monde de Ruby
2: l'équivalent de Nexus en Java ou PyPI en Python
la source
Comme d'autres l'ont dit, une bonne suite de tests unitaires ne résoudra pas votre problème: vous rencontrerez des problèmes lors de la fusion des modifications, même si chaque suite de tests d'équipe réussit.
Idem pour TDD. Je ne vois pas comment cela peut résoudre ce problème.
Votre solution n'est pas technique. Vous devez définir clairement les limites «fondamentales» et attribuer un rôle de «chien de garde» à quelqu'un, que ce soit le développeur principal ou l'architecte. Toute modification du noyau doit passer par ce chien de garde. Il est responsable de s'assurer que chaque sortie de toutes les équipes fusionnera sans trop de dommages collatéraux.
la source
En tant que solution à plus long terme, vous avez également besoin d'une communication meilleure et plus rapide entre les équipes. Chacune des équipes qui utiliseront jamais, par exemple, la fonctionnalité de base Y, doit être impliquée dans la construction des scénarios de test prévus pour la fonctionnalité. Cette planification, en elle-même, mettra en évidence les différents cas d'utilisation inhérents à la fonctionnalité Y entre les deux équipes. Une fois que la fonctionnalité devrait fonctionner, et que les cas de test sont implémentés et convenus, un changement supplémentaire dans votre schéma d'implémentation est requis. L'équipe qui publie la fonctionnalité est nécessaire pour exécuter le testcase, pas l'équipe qui est sur le point de l'utiliser. La tâche, le cas échéant, qui devrait provoquer des collisions, est l'ajout d'un nouveau testcase de l'une des équipes. Lorsqu'un membre de l'équipe pense à un nouvel aspect de la fonctionnalité qui n'est pas testé, ils devraient être libres d'ajouter un testcase qu'ils ont vérifié en passant dans leur propre bac à sable. De cette façon, les seules collisions qui se produiront seront au niveau de l'intention et devraient être clouées avant que la fonction refactorisée ne soit libérée dans la nature.
la source
Bien que chaque système ait besoin de suites de tests efficaces (ce qui signifie, entre autres choses, l'automatisation), et bien que ces tests, s'ils sont utilisés efficacement, détectent ces conflits plus tôt qu'ils ne le sont actuellement, cela ne résout pas les problèmes sous-jacents.
La question révèle au moins deux problèmes sous-jacents: la pratique de modifier le «noyau» afin de satisfaire les exigences des clients individuels, et l'incapacité des équipes à communiquer et à coordonner leur intention d'apporter des changements. Aucune de ces causes n'est à l'origine et vous devrez comprendre pourquoi cela est fait avant de pouvoir y remédier.
L'une des premières choses à déterminer est de savoir si les développeurs et les gestionnaires réalisent qu'il y a un problème ici. Si au moins certains le font, alors vous devez savoir pourquoi ils pensent qu'ils ne peuvent rien faire ou choisissent de ne pas le faire. Pour ceux qui ne le font pas, vous pouvez essayer d'augmenter leur capacité à anticiper comment leurs actions actuelles peuvent créer des problèmes futurs, ou les remplacer par des personnes qui le peuvent. Tant que vous n'aurez pas une main-d'œuvre consciente de la façon dont les choses vont mal, il est peu probable que vous puissiez résoudre le problème (et peut-être même pas à ce moment-là, du moins à court terme).
Il peut être difficile d'analyser le problème en termes abstraits, du moins au début, alors concentrez-vous sur un incident spécifique qui a entraîné un problème et essayez de déterminer comment il s'est produit. Comme les personnes impliquées sont susceptibles d'être sur la défensive, vous devrez être attentif aux justifications égoïstes et post-hoc afin de savoir ce qui se passe réellement.
Il y a une possibilité que j'hésite à mentionner car elle est si peu probable: les exigences des clients sont si disparates qu'il n'y a pas suffisamment de points communs pour justifier un code de base partagé. Si tel est le cas, vous disposez en fait de plusieurs produits distincts et vous devez les gérer en tant que tels et ne pas créer de couplage artificiel entre eux.
la source
Nous savons tous que les tests unitaires sont la voie à suivre. Mais nous savons également qu'il est difficile de les réadapter de manière réaliste à un noyau.
Une technique spécifique qui peut vous être utile lors de l'extension de la fonctionnalité consiste à essayer de vérifier temporairement et localement que la fonctionnalité existante n'a pas été modifiée. Cela peut être fait comme ceci:
Pseudo-code d'origine:
Code de test temporaire sur place:
Exécutez cette version à travers les tests de niveau système existants. Si tout va bien, vous savez que vous n'avez pas cassé les choses et pouvez alors supprimer l'ancien code. Notez que lorsque vous vérifiez l'ancienne et la nouvelle correspondance des résultats, vous pouvez également ajouter du code pour analyser les différences afin de capturer les cas qui, selon vous, devraient être différents en raison d'une modification prévue, comme une correction de bogue.
la source
"La plupart du temps, les changements ne sont pas annoncés par les équipes, donc les bugs sont presque toujours inattendus"
Problème de communication n'importe qui? Qu'en est-il (en plus de ce que tout le monde a déjà souligné, que vous devriez faire des tests rigoureux) pour vous assurer qu'il y a une bonne communication? Que les gens sont informés que l'interface vers laquelle ils écrivent va changer dans la prochaine version et quelles seront ces modifications?
Et donnez-leur accès à au moins une interface factice (avec une implémentation vide) dès que possible pendant le développement afin qu'ils puissent commencer à écrire leur propre code.
Sans tout cela, les tests unitaires ne feront pas grand-chose, sauf lors des étapes finales, il y a quelque chose qui ne va pas entre les différentes parties du système. Vous voulez le savoir, mais vous voulez le savoir tôt, très tôt, et faire en sorte que les équipes se parlent, coordonnent les efforts et aient en fait un accès fréquent au travail que fait l'autre équipe (donc des engagements réguliers, pas un énorme s'engager après plusieurs semaines ou mois, 1-2 jours avant la livraison).
Votre bogue n'est PAS dans le code, certainement pas dans le code de l'autre équipe qui ne savait pas que vous jouiez avec l'interface contre laquelle ils écrivent. Votre bug est dans votre processus de développement, le manque de communication et de collaboration entre les gens. Ce n'est pas parce que vous êtes assis dans des pièces différentes que vous devez vous isoler des autres gars.
la source
Principalement, vous avez un problème de communication (probablement aussi lié à un team building problème de ), donc je pense qu'une solution à votre cas devrait être axée sur ... eh bien, la communication, plutôt que les techniques de développement.
Je tiens pour acquis qu'il n'est pas possible de geler ou de bifurquer le module de base lors du démarrage d'un projet client (sinon vous devez simplement intégrer dans les plannings de votre entreprise certains projets non liés au client qui visent à mettre à jour le module de base).
Il nous reste donc à essayer d'améliorer la communication entre les équipes. Cela peut être résolu de deux manières:
Vous pouvez en savoir plus sur CI en tant que processus de communication ici .
Enfin, vous avez toujours un problème avec le manque de travail d'équipe au niveau de l'entreprise. Je ne suis pas un grand fan des événements de team building, mais cela semble être un cas où ils seraient utiles. Organisez-vous régulièrement des réunions à l’échelle des développeurs? Pouvez-vous inviter des personnes d'autres équipes à vos rétrospectives de projet? Ou peut-être prendre de la bière le vendredi soir parfois?
la source