Vaisseaux sur le moteur à combustion interne. Survivre à la bataille avec la dette technique



Comment survivre à la bataille de la dette technique? Que faire si vous avez un héritage d'une étape sévère? Dans l'article, en utilisant l'exemple de trois cas, je propose de comprendre comment construire le processus de travail avec la dette technique et quelles approches d'ingénierie utiliser pour cela.

Je m'appelle Denis, je suis le chef d'équipe backend chez Wrike. Je suis responsable de la livraison dans mon équipe et de la croissance des développeurs dans plusieurs équipes. Il se trouve que presque toute mon expérience travaille dans la fintech. J'ai travaillé pour deux grandes banques et maintenant je travaille chez Wrike.

Dans les banques, vous apprenez à travailler avec des systèmes où la fiabilité et la tolérance aux pannes sont importantes. Et il y a beaucoup de Legacy là-bas, et j'ai vécu tout cela moi-même en tant que développeur et aidé les autres à me vivre en tant qu'équipes principales.

Wrike est une solution de collaboration en équipe SaaS que nous vendons à nos clients. Nous faisons Wrike en utilisant Wrike pour organiser le développement.

Wrike développe trente équipes de mêlée. Le service est disponible 24/7, donc les décisions que nous prenons doivent être fiables. Si quelque chose ne va pas, cela affectera le travail de presque toutes les équipes.

Pourquoi les vaisseaux spatiaux?


Starship est une métaphore que j'utilise pour décrire ce avec quoi nous programmeurs travaillons. L'application démarre avec plusieurs modules. Puis il commence à croître: une grosse charge lui vient, des microservices apparaissent, des communications entre eux, des intégrations, des API externes, des clients. Il se révèle un vaste écosystème connecté. Plus l'application est grande et ancienne, plus l'écosystème devient connecté. Et plus il est important de maintenir ses nœuds importants à jour et dans un état fiable. Cela devient très triste lorsque les plus importants d'entre eux ne répondent pas aux exigences actuelles ou échouent.
Votre vaisseau ne sautera pas dans la chaîne s'il fonctionne sur AI-95. Le système ne naviguera pas à travers quatre galaxies si l'ordinateur central est Intel Celeron.

Qu'est-ce que la dette technique?


Imaginez que vous ayez une fonctionnalité dans laquelle des bogues apparaissent constamment. Un testeur vient vous voir et vous dit: "Cette semaine, nous avons trouvé quatre nouveaux bugs là-bas." S'agit-il d'une dette technique ou non? Et si la présence de cette fonctionnalité bloque d'autres histoires à faire? Et si c'est juste difficile de travailler avec la solution, et que vous la refactorisez à chaque fois? Si les utilisateurs se plaignent d'une fonctionnalité? Et si elle ne répond pas aux exigences qui lui sont imposées aujourd'hui, ou offense les sentiments des développeurs qui visent l'excellence?

Dans l'article, par dette technique, je comprendrai les fonctionnalités, solutions ou approches qui interfèrent avec le développement ultérieur du produit. Tous les problèmes décrits ci-dessus sont le résultat d'une dette technique. Ce sont des raisons spécifiques: pourquoi il est et pourquoi vous devez travailler avec lui.

Algorithme de dette technique


Pour travailler efficacement avec la dette technique, vous devez poser trois questions fondamentales.

  1. Pourquoi? Pourquoi nous engageons-nous à faire quelque chose avec lui? Pourquoi avons-nous besoin de ce travail? Pourquoi dépenser l'argent de l'entreprise, les heures de développement, le temps des ingénieurs? Il devrait y avoir une compréhension claire des avantages que nous obtiendrons en résolvant un problème particulier. D'après la définition, il est clair que la dette technique bloque le développement du produit. En travaillant avec lui, nous obtiendrons de nouvelles fonctionnalités et capacités.
  2. Quelle? Nous devons clairement comprendre où commence la dette technique, où elle se termine, à quoi elle ressemble et combien de travail devra être fait pour l'éliminer.
  3. Comment? Actions à mener avec la dette technique pour s'en débarrasser.

Pour répondre à la dernière question, il existe un algorithme itératif simple en quatre étapes. La première étape consiste à mettre en évidence la dette technique et à comprendre ses limites. L'étape suivante consiste à le séparer de tout le reste: encapsuler, saisir un contrat de travail entre la solution que vous avez allouée et le reste du système. Après cela, vous pouvez créer une nouvelle solution à proximité, la remplacer et, ainsi, une partie de la dette technique quittera l'application. En répétant plusieurs fois cette itération, vous obtiendrez une solution toute faite.



Voyons maintenant comment l'algorithme fonctionne en réalité en utilisant plusieurs cas comme exemple.

Premier cas


Il existe une application qui fonctionne avec les commandes des clients - système de gestion des commandes. L'application a été écrite il y a longtemps, en 2010, et est construite sur les dernières technologies de l'époque. L'application a fonctionné avec succès en production au cours des 9 dernières années, mais aujourd'hui, l'entreprise comprend qu'il est nécessaire de conquérir de nouveaux marchés et de développer davantage le système. Dans le même temps, il est important de sauvegarder les données et d'augmenter les nouvelles fonctionnalités du système.



Il s'avère qu'il existe des technologies qui sont mortes depuis longtemps, mais il y a aussi des données qui ne peuvent pas être perdues. Toutes les fonctionnalités ne peuvent pas être implémentées dans une application utilisant d'anciennes technologies. Par conséquent, pour être absolument honnête, la situation ressemble à ceci:



Le problème ici n'est pas dans les anciens frameworks, mais dans la situation que nous avons: l'application n'est pas supportée, il est presque impossible de trouver des développeurs pour les frameworks d'il y a une décennie. Nous devons y faire quelque chose.

Lançons l'algorithme. On peut distinguer plusieurs parties de la dette technique et aborder ce processus de manière itérative. Commençons d'abord par Frontend. Nous pouvons lancer le nouveau Frontend en utilisant l'ancien Backend. Nous pourrons étendre le nouveau Frontend, l'adapter aux technologies modernes, il atteindra nos objectifs. Nous pouvons soit compter entièrement sur l'ancien Backend, soit il devra être légèrement modifié pour fonctionner avec le nouveau Frontend. L'étape suivante est l'encapsulation. Avec l'encapsulation, l'architecture nous aide ici. Le point d'encapsulation dans ce cas sera un contrat avec Backend. Après avoir lancé le nouveau Frontend, nous pouvons supprimer l'ancienne partie de Frontend. Maintenant, notre application entière deviendra de plus en plus verte.



La prochaine étape consiste à travailler avec Backend. Ici, le point d'encapsulation sera déjà la couche de base de données. Il s'avère que l'architecture fera à nouveau cette encapsulation pour nous. Et nous pouvons créer une nouvelle solution à proximité, en travaillant avec les mêmes données, et y transférer Frontend. Maintenant, nous avons complètement abandonné l'ancienne solution et pouvons la jeter. Cela nous permet d'atteindre l'objectif que nous nous sommes fixé pour ce projet.



Deuxième cas


Prenez le cas plus délicat. Il existe une application, elle a une fonctionnalité spécifique qui est responsable de l'enregistrement des paires de devises dans la base de données. Par exemple, rouble-dollar, dollar-yen et ainsi de suite. Les informations sont stockées dans une base de données dans une table. Et pour le rendre un peu plus amusant, ajoutez quelques dépendances: il y a un consommateur qui reçoit les données directement de la base de données et un fournisseur de données qui peut les fournir, encore une fois, directement à la base de données.



Nous ne sommes pas satisfaits du format des données et de la manière dont ces données pénètrent dans la base de données. Mais vous devez le corriger soigneusement, il existe de nombreuses dépendances.

Pour ce faire, sélectionnez une pièce spécifique - les données. Vous devez les encapsuler. Pour ce faire, entrez l'intercalaire. Leur tâche est de s'assurer que le consommateur et le fournisseur ne remarquent aucun changement. C'est le sens de l'encapsulation. Nous pouvons maintenant modifier la structure de stockage, car cela n'affectera pas les dépendances externes. Après, nous pouvons créer une nouvelle solution qui enregistre les données dans un nouveau format. Et la dernière étape consiste à transférer les anciennes données vers un nouveau format et à obtenir ce que nous voulions de notre projet: les données dans un nouveau format, et l'ancienne logique peut être supprimée de l'application.


Dans ce processus, les consommateurs de données ne remarqueront pas les changements, ce qui signifie que nous l'avons fait en toute sécurité, tout en conservant la compatibilité descendante. Ensuite, si nécessaire pour le projet, vous pouvez travailler avec le consommateur et le fournisseur de données afin qu'ils utilisent également le nouveau format.

Troisième cas


Pour augmenter l'échelle et comprendre comment cela fonctionne dans les grands projets, imaginez qu'il existe un grand projet, une grande base de code et une sorte de fonctionnalité clé qui se propage à tous les points de l'application. D'autres éléments de Backend l'utilisent, il a accès à l'API publique, c'est-à-dire que les données fuient quelque part. La fonctionnalité est utilisée dans Frontend, dans les systèmes externes et même dans l'appendice va directement de la base de données à l'analyse. Pour rendre l'exemple plus amusant, ajoutez une pincée d'Héritage ici. Enfin un peu.



Deux faits historiques amusants:

  1. Ça marche vraiment.
  2. Personne ne sait comment cela fonctionne. Voilà pourquoi Legacy.

Lorsque vous travaillez avec un tel cas, dans lequel il existe de nombreux points de contact et de nombreuses inconnues, il est utile de comprendre: avec quoi, en effet, nous travaillons, à quoi ressemble cette solution et quelles sont ses capacités. Il est important de comprendre comment la solution que nous voulons retravailler ou dont nous voulons nous débarrasser interagit avec le reste de l'application. Nous devons trouver un terrain d'entente: comprendre comment ils fonctionnent, comprendre leurs contrats pour pouvoir proposer autre chose.

Il existe plusieurs approches ici qui peuvent vous aider, surtout si l'ampleur de la catastrophe est suffisamment grande en termes de quantité de code:

  • . Java , , , . , , , , ;
  • . , , , . , , . , ;
  • -.

Si nous trouvons un terrain d'entente dans le code, vous pouvez les utiliser et envelopper la solution avec des points d'encapsulation. Nous introduisons de nouveaux contrats pour l'interaction de l'application avec notre fonctionnalité.



Ici, la quantité d'héritage a été réduite, car en ce moment même, nous commençons déjà à entrer ce que nous voulons dans l'application.

Ensuite, vous devez faire le premier pas vers une nouvelle solution - les tests. Ils clôtureront le fait très drôle sur Legacy No. 2, que j'ai mentionné plus tôt. Les tests montreront comment fonctionne votre solution. En plus de vérifier le flux de clés, vous devez vous assurer qu'il tombe également exactement là où vous l'attendez de lui, et exactement de la manière que vous attendez de lui.

Il arrive souvent qu'une solution créée une fois à des fins commerciales ait été utilisée à 100%, et aujourd'hui elle ne recoupe les objectifs actuels que de 30%, et 70% - pas. Dans les réalités d'aujourd'hui, ces 70% ne sont plus importants. Les tests que vous avez écrits mettront en évidence les 30%. Après avoir exécuté le test enduit, vous pouvez comprendre quel code n'est pas utilisé du tout, le supprimer et réduire la connectivité et la complexité de votre solution.

Si nous, après avoir écrit les tests et compris comment cela fonctionne, commençons à introduire une nouvelle solution dans la zone encapsulée, nous allons progressivement supplanter tout ce dont nous n'avons pas besoin, supprimer l'héritage et remplacer la solution par une nouvelle qui convient à nos besoins.



La nouvelle solution doit être simple, compréhensible et doit résoudre spécifiquement votre problème, spécifiquement aujourd'hui et pour des objectifs immédiats spécifiques. Il n'est pas nécessaire d'aller plus loin dans la suringénierie, car nous clôturons l'objectif ultime d'aujourd'hui.

Et c'est l'endroit où vous devez vous arrêter, vous accrocher au moment présent et penser: "Pourquoi faisons-nous cela?" À ce stade, vous aurez beaucoup d'informations sur le fonctionnement de la solution, ce qu'elle contient ou non. Vous avez écrit des tests, marqué le code, fait un diagramme, et maintenant vous comprenez mieux un ordre de grandeur. C'est peut-être le point où il vaut la peine de s'arrêter et de comprendre: est-il possible de résoudre un problème beaucoup plus important? Et c'est ce qui a sauvé l'ordre du quartier de développement de notre projet, car nous avons choisi la bonne direction pour le développement du projet.

Comment organiser son travail


Nous adoptons une approche dans laquelle nous essayons de diviser la nouvelle solution en itérations. Ce sont ses pièces spécifiques qui peuvent être déployées en production et qui y changeront quelque chose.

Pour comprendre ce qu'est l'itération et quel ensemble de tâches elle contient, nous avons emprunté le concept de Définition de Terminé à Scrum. Il s'agit d'un ensemble de critères qui doivent être remplis pour qu'une histoire soit considérée comme remplie.

Ici, j'utilise ce concept sous une forme légèrement différente. Par Définition de Terminé, je comprends la description de ce qui changera dans l'application lorsqu'une itération particulière entrera en production.

Souvenons-nous du premier exemple avec le système de gestion des commandes. Dans ce document, l'utilisateur pourrait créer une nouvelle commande en utilisant la nouvelle interface utilisateur et l'ancien backend. C'est le type d'itération - une partie spécifique de la fonctionnalité que nous pouvons déployer en production, et cela fonctionnera. Ou l'utilisateur peut se connecter avec le nouveau modèle de droits d'accès - c'est aussi un changement qualitatif. Vous pouvez donc décrire exactement ce que chaque itération donne dans votre croisade dans la lutte contre la dette technique.

Lorsque vous divisez la solution en itérations, il peut y avoir de nombreux problèmes. Il y aura une dépendance entre eux, on obtient un graphe entier. Dans cette colonne, il y aura plusieurs façons d'obtenir le résultat final. Vous pouvez utiliser la planification inversée - un outil qui contribuera à réduire le temps de travail. Nous partons du point final du projet et posons la question: «Que faut-il faire pour atteindre cet objectif?». Nous comprenons que pour atteindre cet objectif, nous devons passer à l'étape précédente. Ainsi, nous passons de la fin au début et à chaque étape intermédiaire nous répondons à cette question, en passant le chemin critique. Une fois qu'une telle approche nous a sauvé le quartier du développement.

Un outil appelé diagramme de Gantt est bien adapté pour visualiser le travail. Il s'agit d'un diagramme qui montre les relations entre les tâches, leur durée et l'apparence visuelle du projet. Dans l'image est une capture d'écran de Wrike. Nous avons cet outil, nous l'utilisons activement pour travailler avec des projets.



Si vous travaillez sur une solution complète, vous pouvez rencontrer une situation où quelqu'un modifie le code que vous refactorisez, adaptez, dirigez votre processus et que vous venez de penser. Ces changements peuvent rendre difficile le traitement de la dette technique.

Vous pouvez vous protéger de ces changements de plusieurs manières:

  • — . , , - , .
  • , . git hook, git commit git push. unit test, , - , , , : .

Vous pouvez également configurer un système de surveillance externe pour votre code. Chez Wrike, nous utilisons PMD. Le système démarre et vérifie la conformité à certaines règles à chaque nouvelle ligne de code. L'image est un exemple du journal de construction. Ici, la règle «les résultats de la méthode publique doivent être immuables» a été violée, PMD en parle et montre dans quelle ligne - ici c'est la méthode de la «mauvaise méthode». Vous trouverez ci-dessous un indice de ce que vous devez faire pour y remédier. Ainsi, nous savons toujours où la règle est violée et comment y remédier.



Comment traverser le boss final


Une chose importante dont nous n'avons pas parlé est le patron final, qui doit passer par tout en travaillant avec la dette technique. Entreprise, propriétaire de produit, client - tout le monde a un nom différent. Cela peut interférer avec le déplacement de nos initiatives d'ingénierie vers la production.

Nous connaissons tous des cas où le propriétaire du produit n'a pas proposé les solutions les plus optimales d'un point de vue technique. Mais même si votre propriétaire de produit regarde la feuille de titane, vous pouvez toujours négocier avec lui. En travaillant avec la dette technique, vous ouvrez de nouvelles opportunités et le propriétaire du produit en a besoin pour développer son produit.

Vous pouvez vous mettre d'accord sur un quota de temps. Par exemple, 10% du temps que les développeurs consacreront à la dette technique. Un tel quota ne permettra pas de se débarrasser de la dette technique, mais lui permettra de ne pas gonfler.

Évidemment, il est difficile de parler de dette technique, si l'on ne sait pas exactement de quoi parle la conversation. Par conséquent, votre équipe doit avoir un backlog technique, dans lequel il y a des tâches qui sont évaluées et priorisées par les développeurs.

Cependant, les outils ci-dessus ne permettront pas de traiter des projets à grande échelle. Et dans ce cas, il est important, en termes de ce qui a été listé précédemment (3 questions à la dette technique, le projet dans le tracker, le chemin critique dans le graphique, etc.), de pouvoir expliquer les bénéfices du projet à votre client. Et plus l'histoire est élaborée, plus vous lui transmettrez de compréhension. Une entreprise a des objectifs que vous l'aidez à atteindre. Il est important pour vous de vous tenir au courant de ce qui se passe non seulement maintenant, mais aussi des plans qui deviendront réalité dans les trimestres. Par conséquent, communiquez avec l'entreprise, comprenez ce qu'elle veut faire et quels sont les moyens d'atteindre ces objectifs, et utilisez ces connaissances pour combiner vos objectifs d'ingénierie et vos objectifs commerciaux.

Peut-être que les objectifs ne coïncideront pas immédiatement, mais dans un trimestre ou même dans un an. Mais cela vous permettra de comprendre quand l'entreprise sera prête à changer.

Si vous parvenez à synchroniser les objectifs, vous recevrez tous les bonus: priorisation, ressources et organisation de tout le travail. Le propriétaire du produit vous y aidera. La tâche principale est d'être d'accord avec lui.

Total


Lorsque nous parlons de changements dans des domaines clés du produit, un algorithme simple en quatre étapes se complique:



deux autres étapes sont ajoutées. Le premier est de comprendre ce avec quoi nous travaillons. La seconde - après encapsulation - une compréhension du fonctionnement de la solution et de la prévention des modifications de votre code.

En utilisant cet algorithme et mes recommandations pour organiser le processus, vous pouvez travailler avec n'importe quelle dette technique.

L'article est basé sur mon discours lors de la réunion, vous pouvez voir le reportage vidéo .

All Articles