Comment obtenir une intégration continue correcte

Le secret pour livrer à plein régime ne concerne pas vos outils

Par Nathaniel Tetteh sur Unsplash. Voici comment vous réalisez l'intégration continue.

Je suis dans le développement de logiciels depuis de nombreuses années. Vivre à Berlin m'aide également à rester en contact avec une énorme communauté de startups.

J'aime parler à des personnes de différentes entreprises pour savoir à quoi ressemblent leurs processus et leur culture en ce qui concerne les équipes, les pratiques de codage et les normes de qualité.

En ce qui concerne l’intégration continue, il ya une tendance que je remarque le plus souvent. Il n’est pas rare que les développeurs en parlent comme s’il ne s’agissait que de Jenkins, de Travis ou de l’outil qu’ils utilisent.

Apparemment, dans de nombreux environnements, l'intégration continue signifie simplement que vous disposez d'un outil qui vérifie votre dernière validation et vire au rouge ou au vert comme un arbre de Noël, en fonction du résultat des tests.

Je ne suis pas d'accord

Il existe des pratiques et des processus fondamentaux qui précèdent tout outil que vous pourriez utiliser et qui constituent la base même de l'intégration continue.

Sans ceux-ci, l'IC n'est tout simplement pas possible.

Dans ce post, j'en énumère quelques-uns. Je pense qu'ils sont un bon point de départ pour parvenir à une véritable intégration continue.

Si vous êtes dans une équipe qui a du mal à livrer, ou si vous ne parvenez pas à coordonner vos efforts de codage avec ceux de vos pairs, alors cet article est pour vous.

Aucune branche de fonctionnalité

Lorsque les équipes sont confrontées à la tâche de développer une nouvelle fonctionnalité, elles commettent souvent l'erreur d'ouvrir une branche de fonctionnalité dans leur référentiel et de commencer à y lancer de nombreux commits.

L'idée est de s'isoler des contraintes du processus de développement à l'échelle du projet jusqu'à ce que la fonctionnalité soit terminée et puisse être fusionnée dans le maître.

Représentation du flux de travaux git de la branche fonctionnalité

L’idée sous-jacente à cette pratique est plus ou moins la suivante: «si nous travaillons directement sur master et que master est connecté au cycle de publication, nous publierons une fonctionnalité à moitié sauvegardée ou quelque chose qui ne fonctionne pas encore. Pire encore, nous pourrions casser quelque chose car nous n’avons pas complètement testé notre code ».

Bien que cette pratique puisse paraître conservatrice et axée sur la sécurité, c’est totalement le contraire.

Le plus souvent, vous vous retrouvez avec une version cassée juste après la fusion de votre branche. A ce stade, vos seules options sont: a) annuler la validation ou b) réparer la version.

Si vous êtes malchanceux, les deux peuvent être coûteux et propices aux erreurs.

Si votre processus de publication n'est pas suffisamment automatisé, l'annulation d'un si grand commit peut vous coûter une restauration manuelle de la structure de votre base de données, par exemple. Et que ferez-vous si vous altériez de manière destructive vos données?

La réparation à chaud n'est pas moins risquée non plus. Pour minimiser les interruptions de l'expérience utilisateur, vous devez publier un correctif dès que possible. Et nous savons tous que les contraintes de temps ne s’entendent pas du tout avec un développement bien pensé. Le risque est d'ajouter des bugs au-dessus des bugs et d'aggraver la situation au-delà d'une limite acceptable.

Pourquoi les branches de fonctionnalité ne fonctionnent pas

La raison pour laquelle les branches de fonctionnalités ne fonctionnent pas est simple: vous ne recevez pas de commentaires continus pendant le développement.

Si vous et votre équipe ne donnez pas à votre intervenant la possibilité de valider en permanence votre travail en cours, vous perdez le contact avec la réalité.

Vous ne pouvez valider l’évolution de votre développement que si vous laissez votre utilisateur s’enregistrer en permanence.

Dans ce cas, beaucoup de développeurs ont tort de croire que C’est la conviction que s’ils testent suffisamment leur code, soit en écrivant des tests, soit en cliquant manuellement, ils peuvent être assurés que ce qu’ils viennent de développer fonctionnera tout simplement.

Ceci est une erreur et doit être évité à tout prix.

En tant que développeur, vous pouvez essayer de penser comme vos utilisateurs, mais ce n’est tout simplement pas le cas. Les modèles d'utilisation seront négligés et non mis en œuvre. Les comportements ne sont pas prévisibles, ouvrant la porte à des insectes sournois. Les analyses de rentabilisation ou les processus en profondeur ne seront pas découverts.

En d’autres termes, votre logiciel n’implémentera pas au mieux la réalité. Ou il sera tout simplement cassé et ne fonctionnera pas, au pire. Dans les deux cas, cela sera faux aux yeux de l'utilisateur.

Même si le modèle de branche fonctionnelle peut sembler conservateur et axé sur la sécurité, c’est totalement le contraire.

Utiliser les fonctions bascule

La première étape, facile pour atteindre un bon niveau de CI, consiste à utiliser les fonctions bascules.

Une fonctionnalité est un indicateur de configuration dans votre application qui peut activer ou désactiver une fonctionnalité particulière.

Cette technique est incroyablement facile à mettre en œuvre. Dans votre base de code, cela ressemble plus ou moins à ceci.

Les avantages de l'utilisation d'une fonctionnalité bascule sont les suivants:

  • vous pouvez désactiver une fonctionnalité en production sans avoir à la relâcher. Ceci est utile lorsqu'un bogue critique est signalé et que vous souhaitez empêcher les utilisateurs d'accéder à la fonctionnalité endommagée.
  • vous pouvez activer la fonctionnalité dans un environnement canari et la laisser en production. Cela permet aux utilisateurs en début de phase de tester la fonctionnalité et à l'équipe de développement de collecter des informations utiles avant la sortie officielle.
  • vous pouvez activer la fonctionnalité dans un environnement fermé et permettre, par exemple, à un chef de produit de vérifier l'état de la technique, même si la fonctionnalité n'est pas encore complète.

Un autre avantage est débloqué lorsque vous commencez à utiliser les fonctions de bascule.

Étant donné que votre fonction peut être désactivée en production jusqu’à ce qu’elle soit terminée et stable, vous pouvez désormais commencer à fusionner les validations directement dans votre branche principale.

Petits commits, à maîtriser directement

Il existe deux raisons valables de fusionner vos commits directement dans le modèle suivant ce modèle.

Le modèle fusionnant «Jumping Fish».

Qui a fait cela?

La première raison est que vous obtenez un historique plus propre de votre code. Vous savez exactement qui a fait quoi. Cela aide beaucoup lorsque d'autres développeurs ont besoin de clarifications sur une partie spécifique du code.

Avec les branches de fonctionnalités, tous les commits sont regroupés en un seul avant la fusion, de sorte que les informations sont perdues et qu'un seul développeur accepte tout le blâme (git).

Des fusions faciles, moins de conflits, des retours sécurisés

Plus une branche de fonctionnalité est grande, plus il est probable que des conflits de fusion se produisent.

Les conflits de fusion peuvent être très pénibles car les résoudre est un processus purement manuel et extrêmement sujet aux erreurs.

De plus, face à un conflit de fusion, vous devrez probablement retravailler un peu votre logique d'application. Cela se fait généralement à la volée lors du rebasement. Je vous laisse imaginer ce que pourrait être le résultat qualitatif du codage alors que votre cerveau est au milieu d’une autre tâche.

J’ai souvent vu des développeurs dire «Je ne sais pas ce qui s’est passé. J'ai résolu des conflits et je me suis trompé pendant le rebasement ». Peut-être que git a quelques bugs, mais je doute fort que cela puisse gâcher une rebase.

Des commits plus petits vous permettent d’être plus sûrs et d’éviter des rebaisements compliqués. Les conflits sont minimes et triviaux car l'extension de la modification est également superficielle.

Les commits plus petits sont également plus faciles à rétablir en raison d'un facteur psychologique. Lorsque vous travaillez avec de grandes branches, votre biais de coûts irrécupérables va à l'encontre de votre jugement rationnel. Vous êtes réticent ou avez même peur de récupérer une telle quantité de code.

«Mieux vaut essayer de le réparer. Le correctif sera minime par rapport à la quantité de code. Il n’est pas nécessaire de tout annuler après tout le travail que nous avons fait »- Ces mots vous semblent-ils familiers?

Finalement, les petits commits devraient être suffisamment petits pour qu'un membre de l'équipe puisse fusionner quelque chose dans le maître au moins une fois par jour.

Optimiser pour le développement parallèle

C’est là que la conception de logiciels rencontre réellement l’intégration continue.

Même en travaillant sur des branches de fonctionnalités isolées, les membres d’une équipe peuvent se bloquer s’ils ne divisent pas suffisamment le développement d’une fonctionnalité.

Il est ironique que notre logiciel soit découplé et que nos modules soient indépendants les uns des autres, tandis que l’ingénierie de notre processus de développement laisse beaucoup à désirer.

Voici à quoi ressemble une répartition temporelle typique des tâches de développement pour une seule fonctionnalité.

Guybrush doit toujours attendre Elaine pour finir sa tâche sinon il ne peut pas commencer à travailler sur la sienne. Stan reste bloqué jusqu'à ce que LeChuck et Elaine livrent leurs pièces. LeChuck se voit finalement confier une tâche.

Malheureusement, il n’ya pas de solution miracle pour développer une fonctionnalité permettant à chaque membre de l’équipe de travailler sur une tâche en parallèle.

Dans ce cas, il est utile d’examiner la fonctionnalité du point de vue de la conception de logiciels et de tenter de comprendre où se trouvent les lignes de coupe. Je parle essentiellement de la recherche d’interfaces avant de commencer à développer des zones communes.

Une heuristique utile, par exemple, est que si deux développeurs doivent travailler sur deux zones adjacentes, disons un composant frontal et son homologue backend, ils peuvent se mettre d’accord sur l’interface qu’ils vont utiliser pour mettre les deux parties en communication.

Une autre équipe pourrait à la place appliquer «qui arrive en premier décide la règle d'interface», lorsque deux développeurs ou plus commencent à travailler de manière indépendante et que le premier qui arrive à la définition d'un point de contact le spécifie.

Les choses peuvent toujours être changées quand les autres arrivent et constatent que la définition ne correspond pas parfaitement à leur cas d'utilisation. Mais au moins, personne n’est bloqué entre-temps et la nécessité d’améliorer l’interface suscitera probablement une conversation saine entre les membres de l’équipe, ainsi que des informations intéressantes sur la conception de la fonctionnalité.

Conclusion

L'intégration continue ne concerne pas votre outil.

L'intégration continue concerne la manière dont vous concevez votre processus de développement.

Il faut du temps et des efforts pour que tout se passe bien.

Cela implique de comprendre et d'appliquer différentes stratégies de synchronisation entre les membres de l'équipe, mais également toutes les personnes impliquées dans le développement d'une fonctionnalité.

Faire vérifier votre commit par rapport à un pipeline d'automatisation de test n'est que la cerise sur le gâteau.