Comment pouvons-nous éviter le développement axé sur l'IC…?

45

Je travaille sur un très grand projet open source basé sur la recherche, avec un tas d'autres contributeurs réguliers. Étant donné que le projet est maintenant assez important, un consortium (composé de deux employés à temps plein et de quelques membres) est chargé de la maintenance du projet, de l'intégration continue (IC), etc. Ils n'ont tout simplement pas le temps d'intégrer des partenaires externes. contributions bien.

Le projet est composé d'un framework "core", d'environ un demi million de lignes de code, d'un tas de "plugins" gérés par le consortium et de plusieurs plugins externes, dont la plupart ne sont pas disponibles. t même au courant.

Actuellement, notre CI construit le noyau et les plugins maintenus.

Un des gros problèmes auquel nous sommes confrontés est que la plupart des contributeurs (et en particulier les occasionnels) ne construisent pas 90% des plugins maintenus, donc quand ils proposent de refactoriser les changements dans le noyau (ce qui se passe assez régulièrement de nos jours), ils ont vérifié que le code était compilé sur leur machine avant de faire une demande de tirage sur GitHub.

Le code fonctionne, ils sont contents, puis le CI termine la construction et les problèmes commencent: la compilation a échoué dans un plug-in mis à jour par le consortium, le contributeur n'a pas construit sur sa machine.

Ce plugin peut avoir des dépendances sur des bibliothèques tierces, telles que CUDA par exemple, et l'utilisateur ne veut pas, ne sait pas comment, ou ne peut tout simplement pas, pour des raisons matérielles, compiler ce plugin endommagé.

Alors, soit le RP reste à flot dans les limbes des RP jamais fusionnés , soit le contributeur saisit la variable renommée dans la source du plug-in cassé, modifie le code, appuie sur sa branche, attend le CI pour terminer la compilation génère généralement plus d'erreurs et réitère le processus jusqu'à ce qu'il soit satisfait - Ou l'un des deux permanents déjà surbookés du consortium donne un coup de main et tente de corriger le PR sur leur machine.

Aucune de ces options n'est viable, mais nous ne savons tout simplement pas comment le faire différemment. Avez-vous déjà été confronté à une situation similaire de vos projets? Et si oui, comment avez-vous géré ce problème? Y a-t-il une solution que je ne vois pas ici?

lagarkane
la source
84
La règle la plus importante en matière de fourniture d'une API de plug-in à un système est que celui- ci reste stable ou du moins compatible avec les versions antérieures. Les modifications apportées au noyau sans modification intentionnelle de l'API de plug-in ne doivent jamais interrompre la compilation de plug-ins (il peut arriver que la fonctionnalité soit interrompue par accident, mais pas la compilation). Si un simple changement d'un nom de variable à l'intérieur du noyau peut conduire à une compilation cassée d' un plugin , la séparation entre les plugins et le noyau semble être complètement brisée.
Doc Brown
"Public versus Published Interfaces" de Martin Fowler pourrait être une lecture utile.
Schwern
1
@KevinKrumwiede: Je suis sûr qu'ils le savent déjà ;-) Si vous rencontrez des incompatibilités, je suis presque certain qu'ils ont modifié l'API intentionnellement.
Doc Brown
3
Je reformulerais la question, car elle est vraiment trompeuse. Quel genre de problème, comment puis-je gérer les RP quand ils cassent notre CI actuel? capturez mieux votre situation, je pense.
bracco23
2
A quel point votre processus de construction / test est-il difficile / complexe? Il ne devrait s'agir que d'exécuter une seule commande ou de cliquer sur un seul bouton. À ce stade, il devient raisonnable de s’attendre à ce que les utilisateurs effectuent eux-mêmes tous les tests avant de soumettre un PR.
Alexander

Réponses:

68

Le développement piloté par CI, c'est bien! C'est beaucoup mieux que de ne pas exécuter de tests et d'inclure du code erroné! Cependant, il y a plusieurs choses pour rendre cela plus facile pour toutes les personnes impliquées:

  • Définissez les attentes: créez une documentation de contribution expliquant que CI trouve souvent des problèmes supplémentaires et qu'ils devront être résolus avant une fusion. Expliquez peut-être que les changements locaux modestes ont plus de chances de bien fonctionner - il est donc judicieux de scinder un changement important en plusieurs RP.

  • Encouragez les tests locaux: facilitez la configuration d'un environnement de test pour votre système. Un script qui vérifie que toutes les dépendances ont été installées? Un conteneur Docker prêt à l'emploi? Une image de machine virtuelle? Votre testeur dispose-t-il de mécanismes permettant de hiérarchiser les tests les plus importants?

  • Expliquez comment utiliser CI pour eux-mêmes: Une partie de la frustration est que ce retour d’information ne survient qu’après la soumission d’un PR. Si les contributeurs configurent CI pour leurs propres référentiels, ils obtiendront un retour plus tôt et produiront moins de notifications de CI pour d'autres personnes.

  • Résolvez tous les PR, dans les deux sens: si quelque chose ne peut pas être fusionné car il est cassé, et s'il n'y a aucun progrès à faire pour résoudre les problèmes, fermez-le. Ces relations publiques ouvertes et abandonnées encombrent tout, et toute rétroaction vaut mieux que de simplement ignorer le problème. Il est possible de formuler cela très bien et d'indiquer clairement que vous seriez heureux de fusionner lorsque les problèmes seront résolus. (voir aussi: L'art de la clôture par Jessie Frazelle , Meilleures pratiques pour les responsables de la maintenance: apprendre à dire non )

    Pensez également à rendre ces relations publiques abandonnées découvrables afin que quelqu'un d'autre puisse les récupérer. Cela peut même être une bonne tâche pour les nouveaux contributeurs, si les problèmes restants sont plus mécaniques et ne nécessitent pas une connaissance approfondie du système.

Pour la perspective à long terme, ces modifications semblent rompre si souvent des fonctionnalités sans rapport, ce qui peut signifier que votre conception actuelle est un peu problématique. Par exemple, les interfaces de plug-in encapsulent-elles correctement les éléments internes de votre noyau? C ++ facilite la divulgation accidentelle des détails d'implémentation, mais permet également de créer des abstractions fortes qu'il est très difficile d'utiliser de manière abusive. Vous ne pouvez pas changer cela du jour au lendemain, mais vous pouvez guider l'évolution à long terme du logiciel vers une architecture moins fragile.

Amon
la source
13
"Ces RP ouverts abandonnés encombrent tout" Je souhaite que davantage de responsables aient cette attitude
GammaGames Le
34

Pour créer un modèle de plug-in durable, votre infrastructure de base doit présenter une interface stable sur laquelle les plug-ins peuvent compter. La règle d'or est que vous pouvez introduire de nouvelles interfaces au fil du temps, mais que vous ne pouvez jamais modifier une interface déjà publiée. Si vous suivez cette règle, vous pouvez reformuler tout ce que vous voulez pour mettre en œuvre le framework principal sans craindre de casser accidentellement des plugins, qu’il s’agisse d’un consortium géré par un consortium ou d’un tiers.

D'après ce que vous avez décrit, il semble que vous n'ayez pas d'interface bien définie, ce qui rend difficile de dire si un changement va casser les plugins. Travaillez à définir cette interface et à la rendre explicite dans votre base de code, afin que les contributeurs sachent ce qu’ils ne doivent pas modifier.

casablanca
la source
20
CI devrait avoir des tests automatisés. Si vous voulez vous assurer que les plugins ont la même interface, chaque plugin doit contribuer à des tests qui expriment l'interface dont ils ont besoin. Venez-y de cette façon et quand l'interface changera, vous saurez quels plugins vous cassez. Donnez-moi ces tests pour courir localement et je saurai ce que je brise avant de publier le PR.
candied_orange
1
@lagarkane est bien une question de politique que de technique. Il existe des logiciels qui, comme le vôtre, abandonnent simplement le comportement précédent lors d'une mise à niveau. Perl5 n'est pas compatible avec Perl6, Python2.7 n'est pas totalement compatible avec Python3.4, etc. Il existe également des logiciels qui, quoi qu'il arrive, prennent toujours en charge l'ancien code. Vous pouvez toujours exécuter presque tout le code javascript écrit pour Netscape Navigator 4 dans les navigateurs modernes. Le langage de programmation Tcl est compatible avec les mots de passe et remonte à la version originale, etc ...
slebetman
3
@lagarkane: La création du consortium a été un pas dans la bonne direction. Si les membres principaux concentrent leur énergie sur la création de ces interfaces, vous pouvez exploiter le potentiel des futurs docteurs et stagiaires pour que votre projet reste solide tout en minimisant les ruptures. :)
casablanca
4
@Fattie: Cela fonctionne pour Apple car ils construisent des produits destinés aux consommateurs et obligent les développeurs à jouer s'ils veulent en faire partie. Il est peu probable que ces développeurs aiment réellement modifier les modifications, et ce n'est certainement pas un bon modèle pour un projet open source.
Casablanca
1
@casablanca, les lignées MacOS et WindowsOS connaissent un énorme succès. (On peut dire que ce sont les deux plus grands produits en termes d’existence humaine.) Au fil des décennies, ils ont adopté des approches absolument opposées. Apparemment, les deux ont réussi!
Fattie
8

Pour être honnête, je ne pense pas que vous puissiez gérer cela mieux. Si des modifications entraînent la rupture de parties maintenues de votre projet, le CI devrait échouer.

Votre projet a-t-il un projet contributing.mdsimilaire pour aider les contributeurs nouveaux et occasionnels à préparer leurs contributions? Avez-vous une liste claire, quels plugins font partie du noyau et doivent rester compatibles?

S'il est difficile de tout compiler sur une machine en raison de dépendances, etc., vous pouvez envisager de créer des images de docker prêtes à l'emploi en tant qu'environnements de construction utilisables par vos contributeurs.

mhr
la source
1
Merci pour la réponse! Oui, nous avons des directives de contribution disponibles au public, mais il ne répertorie pas les plugins comme vous le suggérez, ce qui serait déjà une bonne idée. Faire des images de docker semble déjà être une grande amélioration du processus de contribution actuel! Merci pour l'entrée
lagarkane
8

aussi, quand ils proposent de refactoriser les changements dans le noyau (ce qui se passe de manière assez courante aujourd’hui), ils vérifient que le code est compilé sur leur machine avant de faire une demande de pull sur github.

Je pense donc que c’est là que le style informel des projets open source peut s’effondrer; la plupart des projets organisés de manière centralisée se méfient du refactoring principal, en particulier lorsqu'il franchit une limite d'API. S'ils refacturent une limite d'API, il s'agit généralement d'un "big bang" dans lequel toutes les modifications sont planifiées en même temps avec une incrémentation de la version principale de l'API, et l'ancienne API est conservée.

Je proposerais une règle selon laquelle "toutes les modifications d'API doivent être planifiées à l'avance": si un représentant principal introduit une modification incompatible en amont de l'API de la part de quelqu'un qui n'a pas été en contact avec les responsables pour convenir à l'avance de son approche, se ferme tout simplement et le déposant a souligné la règle.

Vous aurez également besoin d'un versioning explicite de l'API du plugin. Cela vous permet de développer v2 alors que tous les plugins v1 continuent à se construire et à fonctionner.

Je voudrais également demander un peu plus pourquoi tant de modifications de refactoring et d'API sont apportées. Sont-ils vraiment nécessaires ou juste des personnes imposant leurs goûts personnels au projet?

pjc50
la source
2

On dirait que le processus d'EC doit être plus étroit, plus complet et plus visible pour les contributeurs avant qu'ils ne génèrent un PR. Par exemple, BitBucket a une fonctionnalité de pipelines qui le permet, où vous lui donnez un fichier qui définit en code le processus de construction de l'EC, et en cas d'échec, la branche ne peut pas être fusionnée.

Indépendamment de la technologie, fournir des builds automatiques lorsqu'un contributeur pousse dans une branche leur donnera un retour beaucoup plus rapide sur ce qu'il faut surveiller lors de la modification et conduira à des PR qui n'ont pas besoin d'être corrigés après coup.

Il serait bon de résoudre les problèmes de conception, mais ils sont orthogonaux à ce problème.

Nathan Adams
la source
2

Le code fonctionne, ils sont contents, puis le CI termine la construction et les problèmes commencent: la compilation a échoué dans un plug-in mis à jour par le consortium, le contributeur n'a pas construit sur sa machine.

Ce plug-in peut avoir des dépendances sur des bibliothèques tierces, telles que CUDA par exemple, et l'utilisateur ne veut pas, ne sait pas comment, ou ne peut tout simplement pas, pour des raisons matérielles, compiler ce plugin endommagé.

Votre solution est simple: abaissez la barrière à la contribution .

Le moyen le plus simple pour (1) accélérer le cycle édition-compilation-test et (2) atténuer les différences d'environnement consiste à fournir des serveurs de génération :

  • Choisissez des machines robustes: 24, 48 ou 96 cœurs, 2 Go de RAM / core, SSD, pour accélérer la compilation.
  • Assurez-vous qu'ils disposent du matériel approprié: FPGA, carte graphique, tout ce dont vous avez besoin.
  • Créez une image Docker avec toutes les bibliothèques de logiciels nécessaires pré-installées.

Et ouvrez ensuite ces serveurs de construction aux contributeurs. Ils devraient pouvoir se connecter à distance à une nouvelle image Docker et éditer, compiler-tester à distance sur cette machine.

Ensuite:

  • Ils n'ont aucune excuse pour ne pas construire / tester les plugins maintenus: ils ont tout à leur disposition.
  • Ils n'ont pas besoin d'attendre de longs commentaires avec les PR basés sur l'IC: ils ont une compilation incrémentielle et la possibilité de déboguer (plutôt que de deviner).

En règle générale, les serveurs de build peuvent être partagés par plusieurs contributeurs. Toutefois, lorsque des périphériques matériels spéciaux sont impliqués, il peut être nécessaire qu'un contributeur utilise ce périphérique par lui-même.


Source: travaillant sur des logiciels utilisant des FPGA, étant donné le prix des bêtes et la variété de modèles dont nous avons besoin, vous ne trouvez pas chaque modèle de FPGA installé sur chaque ordinateur de développeur.

Matthieu M.
la source
1

Si le fait de contribuer au cœur sans changer aucun contrat peut rompre un logiciel dépendant, cela suggère que:

  • Les contrats de vos interfaces peuvent être ambigus. Peut-être qu’ajouter des attributs à vos fonctions et à leurs paramètres aiderait à exposer des contraintes supplémentaires au code client afin de clarifier les contrats. Ou si vous appliquez des modifications qui rompent les contrats, peut-être que l’adoption de versions sémantiques peut vous aider.
  • Les tests unitaires ne couvrent pas suffisamment les scénarios d’appel possibles.

L’un ou l’autre problème doit être facile à résoudre, mais vous indiquez que l’équipe centrale n’est peut-être pas en mesure de le faire. Une option serait de demander l'aide de la communauté pour résoudre le problème.

jcayzac
la source
1

Personne d'autre ne semble avoir évoqué ce problème comme une solution potentielle.

  • liste tous les plugins auxquels vous pouvez accéder.
  • exécuter tous les tests définis par ces plugins
  • enregistrer toutes les requêtes / réponses / interactions entre le noyau et tous les plugins
  • stocker ces enregistrements, ce sont maintenant des tests de compatibilité approximatifs.

Lors du développement du noyau, encouragez les développeurs à exécuter ces tests de compatibilité. S'ils échouent, ne vous enregistrez pas.

Cela ne garantira pas la compatibilité à 100%, mais cela posera beaucoup plus de problèmes très tôt.

Un autre avantage est que ces enregistrements peuvent indiquer quelles interfaces sont activement utilisées et quelles fonctionnalités sont activement utilisées.

Kain0_0
la source
0

J'ai du mal à comprendre la situation telle qu'elle semble être: l'IC ne construit qu'une seule branche?

Y a-t-il une raison pour laquelle vous ne pouvez pas créer plus d'une branche avec CI?

La solution la plus simple à ce problème serait de permettre à tout contributeur d’exécuter le build CI sur sa branche de fonctionnalité .

Ensuite, vous devez simplement disposer d'un CI réussi sur la branche fonctionnelle pour que la demande d'extraction de cette branche soit acceptée.

Kyralessa
la source
Cela semble résumer le problème.
Fattie
1
La Question dit "Ou le contributeur [...] modifie le code, appuie sur sa branche, attend que l'EC termine sa compilation, génère plus d'erreurs et réitère le processus jusqu'à ce que CI soit heureux" - je pense donc que cela est déjà le cas, mais le problème est qu’il est quelque peu pénible de développer avec un cycle d’édition-débogage aussi long.
npostavs
@ npostavs Merci, je suppose que c'est ce que j'ai manqué la première ou les deux premières fois que je l'ai lu. Malgré tout ... je suppose que je ne semble pas être le problème. Il y a beaucoup de dépendances, elles ne peuvent pas être brisées, donc un contributeur doit rester compatible avec toutes. C'est la nature des gros logiciels. Certes, un travail pourrait être fait pour accélérer la construction, peut-être, mais sinon, quel raccourci pourrait-il y avoir?
Kyralessa