Comment refactoriser lorsque tout votre développement se fait en agence?


24

Dans mon entreprise, tous nos développements (corrections de bugs et nouvelles fonctionnalités) se font sur des branches distinctes. Une fois terminé, nous l'envoyons à QA qui le teste sur cette branche, et quand ils nous donnent le feu vert, nous le fusionnons dans notre branche principale. Cela pourrait prendre entre un jour et un an.

Si nous essayons de resserrer tout refactoring sur une branche, nous ne savons pas combien de temps il sera "hors", donc cela peut provoquer de nombreux conflits lorsqu'il est fusionné.

Par exemple, disons que je veux renommer une fonction parce que la fonctionnalité sur laquelle je travaille utilise beaucoup cette fonction, et j'ai trouvé que son nom ne correspond pas vraiment à son objectif (encore une fois, ce n'est qu'un exemple). Alors je fais le tour et trouve chaque utilisation de cette fonction, et je les renomme tous sous son nouveau nom, et tout fonctionne parfaitement, alors je l'envoie à QA.

Pendant ce temps, de nouveaux développements se produisent, et ma fonction renommée n'existe sur aucune des branches qui sont dérivées de la principale. Lorsque mon problème sera fusionné, ils vont tous se casser.

Y a-t-il un moyen de gérer cela?

Ce n'est pas comme si la direction approuvait un problème de refactorisation uniquement, il doit donc être intégré à d'autres travaux. Il ne peut pas être développé directement sur le principal car tous les changements doivent passer par l'AQ et personne ne veut être le con qui a cassé le principal afin qu'il puisse faire un peu de refactoring non essentiel.


Quel contrôle de version utilisez-vous? Il existe différentes approches pour DVCS et un modèle de serveur centralisé. De plus, de quoi sont délaissées les branches de développement? Si une branche de fonctionnalité est acceptée, comment les autres branches de développement prennent-elles en compte les modifications?

2
En passant, un diagramme de la structure de branchement actuelle pourrait être très utile. Il est tout à fait possible que la racine du problème avec la difficulté de refactoring soit en partie causée par certaines ... politiques de branchement non conventionnelles (voir programmers.stackexchange.com/questions/210360 pour un tel exemple). Je suggérerais également de lire vance.com/steve/perforce/Branching_Strategies.html pour obtenir quelques idées et informations (si je suis en mesure de répondre à cette question, ce sera un point de référence majeur).

1
Le dernier paragraphe le résume - si l'entreprise ne perçoit pas la valeur, il n'y a aucun moyen pour un refactor majeur de poursuivre. Vous devez travailler avec votre équipe de test pour résoudre leurs délais. (Je soupçonne que votre assurance qualité est vraiment un test de résistance (ils mettent une perruque et un rouge à lèvres et font semblant d'être quelque chose qu'ils ne sont pas). Une vraie équipe d'assurance qualité vous dirait quoi refactoriser, sans vous
gêner

1
@mattnz: Vous avez tout à fait raison. Ce n'est pas une vraie équipe d'AQ. Il s'agit principalement du support client. Je pense que beaucoup de leurs responsabilités devraient être transférées à l'équipe de développement, car ils ne peuvent tout simplement pas gérer tout ce que nous leur confions, mais c'est un problème de gestion et une bataille que je n'ai pas encore gagnée.
mpen

3
Vous avez raté ma fouille. Test! = QA. L'AQ supervise la qualité et vise à améliorer les résultats commerciaux. Le test tente de prouver l'absence de défauts en les détectant.
mattnz

Réponses:


12

Il y a plusieurs problèmes qui se mélangent pour rendre le refactoring difficile dans cet environnement. À cela s'ajoutent des problèmes non techniques ("mais c'est un problème de gestion et une bataille que je n'ai pas encore gagnée").

Le premier problème à examiner est la branche longue. Ces branches ont du mal à suivre les modifications en dehors de la vue du développeur. Pour remédier à cette:

  • Lorsque le code est terminé - donnez-lui une fois de plus (laissez le support client le consulter s'il le souhaite), mais fusionnez-le rapidement dans le développement afin que d'autres modifications qui en dépendent puissent être récupérées et les modifications en conflit identifiées tôt Dans le processus.
  • Si, pour une raison quelconque, un brach devient long pendant la refactorisation, il est généralement recommandé de fusionner de stable dans la branche pour détecter les modifications et la refactorisation. Souvent, cela minimise les conflits et les surprises lors de la fusion de la branche de fonctionnalité dans la branche stable.
  • Tous les tests d'intégration doivent être effectués sur les versions - pas sur les fonctionnalités . Dans cet environnement, les fonctionnalités peuvent ou non être entièrement intégrées au système. Bien qu'il soit possible d'effectuer un contrôle d'intégrité sur la fonctionnalité de manière isolée, il n'identifie pas les problèmes lors de la publication.
  • Du moment de l'achèvement du code à la fusion (appelons-le develop - branchement de master / stable / release a ses propres problèmes de ne pas prendre en compte les derniers changements de développement) ne devrait pas être trop long. Plus vous attendez, plus les connaissances sont perdues et plus il est difficile pour le code de s'intégrer à d'autres lignes de code.

Un autre problème qui se mélange à cela est que j'ai fait allusion aux points ci-dessus est le rôle changeant de la branche au fil du temps. Il commence comme une branche de développement où les développeurs s'engagent, puis devient une zone de test (quels tests sont effectués ici qui peuvent être significatifs dans l'ensemble de l'application?), Qui est ensuite fusionné en stable (et vraisemblablement publié - est-ce testé à nouveau?).

Avec une fonction de début à la fin plus courte, il est plus facile pour le refactoring d'être repris par d'autres succursales.

Encouragez les développeurs à obtenir l'environnement entier. De simples changements de sélection peuvent conduire à ... disons des environnements de développement intéressants. Bien que la sélection des cerises ait ses utilités, le fait que ce soit le mode par défaut de tirer des modifications dans une branche peut être inquiétant.

La refactorisation est quelque chose qui, idéalement, se fait en permanence, ou sinon constamment chaque fois qu'il y a un minimum de temps d'arrêt. Branch, faites un simple refactoring, exécutez les tests unitaires pour vérifier que tout fonctionne toujours (son unité testée, non? Non? ), Puis fusionnez à nouveau dans stable. Faites circuler les informations pour que les autres développeurs tirent ces modifications que vous avez refactorisées dans leurs propres branches.

Il est important que les développeurs possèdent la qualité du code. Alors que la direction des fonctionnalités vient de l'extérieur et que les allocations de temps ne sont souvent pas les nôtres, la qualité du code est une chose dont il faut être fier et prendre du temps.

Vous pouvez trouver les questions suivantes utiles dans la quête d'allocation de temps pour faire face à la dette technique:

Vous pouvez également consulter des outils tels que le sonar qui peuvent aider à identifier les zones du code qui nécessitent le plus de travail pour la refactorisation. le plugin technique de la dette est quelque chose qui peut être utilisé pour signaler l'accumulation de dette au fil du temps dans la base de code.

Il est souvent nécessaire de souligner que le retour sur investissement pour traiter la dette technique est un délai d'exécution plus rapide pour les fonctionnalités et les corrections de bogues de l'équipe de développement.


Les tests sont essentiellement effectués à trois moments dans le temps. Une fois lorsque le problème est déclaré résolu (pour vous assurer qu'il répond à toutes les exigences et qu'il n'y a pas de problèmes majeurs), à nouveau lorsqu'il est fusionné à nouveau par défaut (test d'intégration), et à nouveau lorsque nous faisons une construction (intégration avec tous les éléments sélectionnés) problèmes / examen final). Je pense que la sélection des cerises est nécessaire dans notre environnement car nous exploitons un SaaS avec des clients très particuliers. Je vais jeter un œil à ces liens, merci pour les pointeurs! Edit: Il y a en fait une autre recherche sur la production pour s'assurer qu'elle a bien fonctionné.
mpen

3

Habituellement, je développe une version refactorisée en "parallèle" avec la version actuelle, c'est-à-dire dans la même base de code, mais sans la référencer depuis l'application principale. Et quand une nouvelle solution est faite et testée, je commence une refactorisation réelle.

Exemple 1. Supposons que j'ai Thing, que ce soit une fonction, une interface, un module ou autre. Et je veux le refactoriser. Je crée Thing2 dans la même base de code, c'est une version refactorisée de Thing. Quand c'est fait et testé, je refactorise tout ce qui fait référence à Thing, pour le remplacer par Thing2. Habituellement, cette étape prend relativement peu de temps.

Si la refactorisation réelle prend trop de temps pour rester synchronisée sans visser l'équipe, je prends toutes les fonctionnalités pertinentes et les refactore en parallèle également.

Exemple 2. J'ai un nouveau backend de rendu, c'est la version refactorisée de l'ancien. Mais il n'est pas compatible avec l'ancien frontend de rendu. J'ai donc besoin de refactoriser le frontend. Et encore: dans la même base de code. Lorsque tout est terminé, je ne fais que changer de classe d'instance frontend, idéalement cela prendra un court commit.

Oui, récursivement, on peut conclure que tout doit être fait en parallèle. Mais cela se produit généralement lorsqu'il y a trop de couplage dans la base de code ou qu'il change trop rapidement.

Enfin, lorsque le nouveau code est intégré et fonctionne correctement, les anciennes fonctionnalités peuvent être supprimées de la base de code et les nouvelles fonctionnalités peuvent être renommées pour obtenir d'anciens noms.

En règle générale, l'idée est de préparer de nouvelles fonctionnalités en parallèle et de passer à leur utilisation par une petite étape.

John Carmack utilise cette approche (ou du moins similaire), peut-être que son article de blog l'explique mieux: (lien)


C'est une bonne approche. J'essaie de me souvenir de ce qui a vraiment suscité cette question maintenant ... Je ne pense pas que c'était quelque chose de très propice à la parallélisation. Ou si c'était le cas, je pense que ma préoccupation est que cette approche provoque beaucoup de fragmentation dans la base de code. Nous avons de «vieilles façons» de faire les choses et de «nouvelles façons» de faire les choses, et les anciennes choses sont remplacées à un rythme glacial, mais cela cause des maux de tête aux développeurs car maintenant, ils doivent essentiellement connaître deux (ou plus) systèmes.
mpen

1

Cela peut ressembler à une difficulté du côté technique alors qu'en fait, c'est du côté des exigences.

Là où le développement est orienté vers différentes exigences dans différentes branches, c'est la vraie difficulté. Les gestionnaires et les architectes de l'équipe doivent prendre des décisions qui peuvent permettre aux différents besoins de l'entreprise de coexister.

Le processus ZBB et les "compromis" Co-Dev lorsqu'ils sont effectués après avoir pris les bonnes décisions avec les contributions pertinentes de tous les développeurs devraient vous permettre de mettre en œuvre ce dont vous avez besoin sans avoir à réfléchir - Comment vais-je fusionner mon code.

ZBB signifie Zero-based budgeting . En disant Co-Dev, je voulais dire peu de gens qui travaillent en programmation parallèle.


2
que sont "ZBB" et "Co-Dev"?
moucher

ZBB - en.wikipedia.org/wiki/Zero-based_budgeting . En disant Co-Dev, je voulais dire peu de gens qui travaillent en programmation parallèle.
Yosi Dahari

1

Le problème me semble que vous travaillez trop longtemps sur les branches. Le coût des conflits augmente de façon exponentielle avec la durée pendant laquelle tout le monde reste sur une branche, donc avec de très longs conflits, vous avez peu de chances de refactoriser.


0

Votre problème est le modèle de branche que vous utilisez. Vous pouvez développer sur une branche, et lorsqu'elle est terminée et prête pour le contrôle qualité, la branche est fusionnée dans un «tronc intermédiaire», parfois appelé intégration ou test. Lorsque vous développez la fonctionnalité suivante, vous pouvez à la place partir de ce tronc intermédiaire.

Ce modèle vous permet de développer plusieurs fonctionnalités en parallèle sur différentes branches, en les fusionnant toutes ensemble sur la branche d'intégration pour les envoyer à QA, et également de maintenir un seul tronc de versions (vous fusionnez le QA de base de code reçu au tronc principal lorsqu'ils le certifient) )

Vous faites l'hypothèse que vos modifications apportées à QA seront transmises sans modification majeure - si le code QA revient avec des instructions pour supprimer la moitié des modifications, vous devrez revenir en arrière, mais si cela ne se produit pas, cela fera votre développement beaucoup plus fluide. Donc, vous prenez essentiellement des branches pour les nouvelles fonctionnalités de ce que sera votre code principal (c'est-à-dire le tronc après la fusion du code passé à QA), plutôt que ce qu'il est aujourd'hui (c'est-à-dire le tronc actuel) et donc ne développez plus par rapport à la base de code de la version précédente .

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.