Notre équipe est responsable de l'exploitation et du développement d'un grand produit d'entreprise.
Début 2017, prenant une pause dans une implémentation majeure et relisant les «leçons apprises», nous avons fermement décidé de revoir le développement et la livraison de notre application. Nous étions inquiets de la faible vitesse et de la qualité de livraison, ne nous permettant pas de fournir le niveau de service que les clients attendent de nous.
Il était temps de passer des paroles aux actes - de changer les processus.
Cet article parlera brièvement de l'endroit où nous avons commencé, de ce que nous avons fait, de la situation actuelle, des difficultés que nous avons rencontrées, de ce que nous devons laisser derrière nous, de ce que nous prévoyons de faire d'autre.
Commencer
Un peu sur le système
L'application est un exemple classique d'une application d'entreprise monolithique du «déversement architectural des années 2000»:
- Exploité et développé sur 15 ans.
- Il s'agit d'un ensemble d'une demi-douzaine de WinForms, de services Windows et d'applications ASP .Net liés à une seule base de données MS SQL.
- Taille de la base de code: ~ 1MLOC pour C #, ~ 9000 objets de base de données. Une grande partie de la logique métier s'exécute côté base de données.
- L'application se compose de ~ 250 + solutions pour créer un client win / web (une solution par groupe de formulaires associés). Il s'agit d'un héritage du processus de développement et de l'architecture client précédents.
- L'application prend en charge plusieurs types de processus (clients) en modifiant la configuration interne: définition des processus, autorisations, champs flexibles, etc. dans les tables de configuration de la base de données système. Dans le même temps, la base de code d'application est la même pour tous les clients.
- L'application est déployée et prise en charge sur plus de 25 sites (chaque site est une instance indépendante du système) et dessert un total de plusieurs milliers d'utilisateurs finaux dans différents fuseaux horaires.
- Le développement et l'assemblage de l'application finie et de ses composants sont effectués par l'entrepreneur.
- le code a été stocké du côté de l'entrepreneur (version locale de MS TFS). Le code est transmis au client sur une base mensuelle sous la forme d'une archive de la version actuelle de la branche du référentiel principal.
- la livraison a été effectuée par la livraison de "mises à jour delta": pour l'application (ensemble de dll, exe, etc.) et les composants de la base de données (ensemble de scripts sql create / alter). L'application a été construite et les packages delta préparés par l'entrepreneur.
- le processus de déploiement a été pris en charge par le système de transport, les modifications ont été appliquées automatiquement.
La livraison est effectuée dans le cadre de sorties mensuelles (comme je l'ai arrangé, je vous l'ai dit plus tôt ici ).
Problèmes existants
Manque de contrôle
- Malgré la propriété officielle du code, l'assemblage réel de l'application par le client était impossible.
- de ce fait, il est impossible de vérifier l'opérabilité du code transmis au client.
- changements dans le code - pas transparent pour le client. Il n'est pas possible de faire correspondre les modifications demandées et réelles du produit.
- l'analyse de code est difficile pour SQL et impossible pour les composants C #
Entrée de main-d'œuvre et erreurs
- la préparation de "packages delta" est une procédure de développement longue, source d'erreurs et de certains coûts de projet.
- Le déploiement d'une application Delta Packet nécessite de suivre l'ordre des packages. Une erreur de panne de paquet est un problème de déploiement majeur et une source importante d'incidents.
- Des régressions se produisent régulièrement: des erreurs qui semblent avoir été réparées et des corrections apportées au produit sont réapparues.
Limitations
- la possibilité de restaurer l'état du système à un moment dans le passé (changements de restauration) est pratiquement absente.
- la capacité de faire évoluer efficacement les ressources de développement et de test précoce en attirant les employés des clients est pratiquement absente.
Résultats attendus
Au début du projet, nous nous sommes fixés des objectifs évidents pour résoudre les problèmes identifiés ci-dessus.
- Transférer le référentiel de codes vers le contrôle client
- Déplacer le processus de création d'application du côté client
- Modifier le processus de distribution des changements, abandonner le «delta des changements» au profit d'une mise à jour complète
De plus, en utilisant les solutions obtenues lorsque les deux premiers objectifs ont été atteints, nous avons calculé:
- Améliorez la qualité technique des solutions obtenues grâce au contrôle du code
- Augmentez l'engagement et la convivialité des tests en fournissant un déploiement en libre-service.
Étapes d'un long chemin
Analyse de l'état actuel des processus de développement
Première étape: analyser le processus de développement de l'entrepreneur existant. Cela a aidé à planifier les changements afin que, si possible, ne pas interrompre le travail.
Malheureusement, la connaissance du processus de développement a montré que, dans la compréhension de l'industrie informatique à l'heure actuelle, le processus était absent.
- Le code de la base de données et sa logique métier n'étaient pas mis à jour dans le référentiel. La raison principale: le manque d'outils qui implémentent l'assembly à partir du code dans le référentiel et le déploiement du résultat. Ainsi, le code dans le référentiel n'est que de la documentation.
- La "vraie" version du code de la base de données se trouve dans la "base de données de développement" commune, sur laquelle travaillent des dizaines de développeurs.
- Le code d'application client (C #, ASP.NET) a été conservé dans le référentiel, mais la qualité et la rapidité des validations n'étaient pas garanties.
- L'assemblage des composants (pas la totalité de l'application) a été réalisé dans les stations du développeur. Il n'est pas entièrement clair comment le code a été mis à jour avant l'assemblage. Le composant assemblé a été disposé sur un dossier partagé partagé. De là, un «paquet delta» a été formé pour le client.
- L'absence totale de pratique de maintien des branches de développement. Par des signes indirects, nous le soupçonnions depuis longtemps - mais après avoir plongé dans le processus, tout est devenu évident.
Passer à un nouveau référentiel et à un nouveau système de contrôle de version
La dépendance à l'égard des plates-formes MS et des normes d'entreprise a déterminé le choix de l'environnement de développement - Team Foundation Server.
Cependant, au moment où nous avons démarré le projet directement (avril 2017), la version de Visual Studio Team Services venait d'être publiée. Le produit semblait très intéressant, il était désigné comme une orientation stratégique pour MS, il offrait des référentiels git, l'assemblage et le déploiement sur site et dans le cloud.
Le TFS d'entreprise sur site était à la traîne par rapport à la version et aux fonctionnalités de VSTS, la migration vers la nouvelle version n'était qu'en cours de discussion. Nous ne voulions pas attendre. Nous avons décidé de passer immédiatement à VSTS, car cela réduisait nos frais généraux pour la prise en charge de la plate-forme et nous donnait un contrôle total sur la façon et ce que nous faisons.
Au moment du début des changements, l'équipe de développement avait de l'expérience avec TFSVC, le code de l'application était stocké dans un tel référentiel. D'un autre côté, GIT est depuis longtemps devenu la norme pour la communauté informatique - les clients et les consultants tiers ont recommandé de passer à ce système.
Nous voulions que l'équipe de développement soit impliquée dans la décision d'un nouveau système de contrôle de version et fasse un choix éclairé.
Nous avons déployé deux projets dans VSTS avec différents référentiels - TFSVC et GIT. Un ensemble de scénarios a été défini, qui a été proposé pour tester et évaluer l'utilisabilité dans chacun des systèmes.
Parmi les scénarios évalués figuraient:
- Créer et fusionner des branches
- Organisation de travaux communs (sur une ou plusieurs branches)
- Modifier les opérations de la chaîne (validation, annulation)
- Intégration tierce
- La possibilité de continuer à travailler lorsque le serveur n'est pas disponible.
En conséquence, comme prévu, GIT a été choisi, et jusqu'à présent, personne ne l'a regretté.
En tant que processus, nous avons commencé à utiliser GitFlow. Ce processus a fourni suffisamment de contrôle sur les modifications et a permis la livraison des versions, comme nous en avons l'habitude.
- Nous avons défendu la branche de développement avec une politique qui exigeait que tous les changements passent par des demandes de tirage.
- Nous essayons d'adhérer à la pratique de "un ticket - une demande de retrait". Les changements de différents billets ne sont jamais combinés en un seul changement. Nous essayons de faire de notre mieux pour tester sur la branche de fonctionnalité afin d'éviter la situation avec des corrections dans les requakes de pull suivants.
- Lors de la fusion en développement, toutes les modifications sont fusionnées en un seul commit (squash).
- Les branches de version sont créées à partir de develop.
- Si nécessaire, dans la branche de publication, vous pouvez ajouter les dernières modifications de manière sélective (cerise sur le gâteau) ou toutes (rebase). Nous n'effectuons pas la correction directement dans la branche de publication.
- Après avoir déployé la dernière version du produit, il passe au master via push force (seules quelques personnes ont ce droit)
Automatisation de l'assemblage des produits
L'application était un grand nombre d'assemblages, des centaines de solutions. Comme il s'est avéré lors de l'audit du processus, tout cela a été collecté séparément et «manuellement».
À la première étape, nous avons décidé de ne pas tout refaire à partir de zéro (afin de ne pas arrêter la livraison existante), mais de "boucler" l'assemblage dans un ensemble de scripts msbuild - un script par composant.
Ainsi, nous avons rapidement obtenu des scripts qui exécutaient tous les artefacts intermédiaires nécessaires, et finalement - le produit fini.
Une histoire distincte est une conception de base de données. Malheureusement, le système contient plusieurs composants CLR qui n'étaient pas bien structurés. Les dépendances ne permettent pas une base de déploiement simple avec du contenu. Pour le moment, cela est résolu par un script de pré-déploiement.
De plus, en raison du paysage système inégal (les versions SQL Server 2008 et 2014 ont été installées à différents points), il était nécessaire d'organiser l'assemblage du projet de base pour les versions .Net 2.0 et 4.0.
Une fois tous les scripts prêts et testés, ils ont été utilisés dans le script de construction VSTS.
Immédiatement avant le début de l'assemblage, les versions de tous les produits ont été mises à jour vers un numéro standard commun, y compris le numéro de build-through de la build. Le même numéro a été stocké dans le script de post-déploiement. Ainsi, tous les composants - la base de données et toutes les applications clientes - sont sortis cohérents et numérotés également.
Déploiement sur banc d'essai
Une fois la version initiale du processus de génération terminée, nous avons procédé à la préparation du script de déploiement.
On s'attend à ce que la base de données soit la plus gênante.
Le déploiement d'une copie «top» d'une vraie base de données a révélé de nombreux conflits entre l'assemblage et l'état des systèmes réels:
- Versions incohérentes dans GIT et dans le système réel
- Schémas de base de données appartenant à des utilisateurs dont la suppression était prévue.
Stabilisation du processus de développement
Bien sûr, il est étrange d'en parler, et plus encore d'écrire ici, mais le changement le plus sérieux pour les développeurs a été l'introduction du principe "si ce n'est pas dans git, cela n'existe pas". Auparavant, le code était validé «pour avoir fait rapport au client». Maintenant - sans cela, il est impossible de livrer quoi que ce soit.
Le plus dur, c'était avec le code de la base de données. Après avoir basculé vers le déploiement de la base de données à partir du référentiel, via l'assemblage et le déploiement à l'aide de sqlpackage, l'approche "delta" a été remplacée par l'approche "état souhaité". Les packages appartenaient au passé, tout devait être déployé automatiquement.
Mais! Jusqu'à la transition complète vers le nouveau processus de déploiement, les modifications devaient encore être apportées. Et il était nécessaire de le faire à l'ancienne - les «mises à jour delta».
Nous avons été confrontés à la tâche d'assurer une cohérence complète et constante de l'état du système lors de la livraison des paquets delta et du contenu du référentiel.
Pour ce faire, nous avons organisé le processus suivant:
- Régulièrement, le code du référentiel a été collecté et déployé dans une base de données "modèle" vide.
- Sur la base de la base «modèle», un autotest spécial était en préparation. Pour chaque objet de la base de données «modèle», des sommes de contrôle ont été calculées. L'autotest contient toutes ces sommes de contrôle et au démarrage, il calcule les sommes de contrôle des objets correspondants de la base de données "vérifiée". Toute divergence dans la composition des objets ou leurs sommes de contrôle entraîne une baisse du test.
- Le test de «chute» a automatiquement interdit le transfert de colis depuis l'environnement de test plus bas dans le paysage. Cette intégration a déjà été mise en œuvre dans le système de transport précédent.
Ainsi, grâce au contrôle automatique, il a été possible de mettre à jour relativement rapidement le code de la base de données produits dans git et de le maintenir sans effort supplémentaire de la part de l'équipe projet. Dans le même temps, les développeurs ont commencé à s'habituer à la nécessité de valider correctement et en temps opportun le code dans le référentiel.
Déploiement de produits sur des environnements de test d'intégration
Après avoir terminé l'étape précédente, nous avons procédé directement au déploiement de l'application sur un environnement de test. Nous avons complètement arrêté d'appliquer les packages delta aux systèmes de test et sommes passés au déploiement automatique à l'aide de VSTS.
À partir de ce moment, toute l'équipe a commencé à recevoir les premiers fruits des efforts déployés plus tôt: le déploiement s'est déroulé sans efforts supplémentaires. Le code personnalisé a été automatiquement collecté, déployé et testé.
Malheureusement, comme nous l'avons compris plus tard, «l'alignement du référentiel» a conduit au fait que nous avions une version de la version stable de «develop», mais la version de «production» n'était toujours pas disponible. Et donc, il n'y avait rien pour aller au-delà de l'environnement de test avec QAS et PRD.
Le code d'application côté base de données peut être comparé à celui productif et comprendre les différences. Il n'y avait rien à comparer avec les applications clientes - il n'y avait qu'une version productive à jour sous la forme d'un ensemble de fichiers exécutables, et à partir de laquelle ils ont été compilés, il était impossible de le dire avec certitude.
Test du produit suite à un assemblage automatique
Après avoir changé l'approche de l'assemblage, le produit a dû subir des tests de régression approfondis. Il fallait s'assurer que l'application fonctionnait et que rien n'était perdu.
Lors des tests, il s'est avéré être plus facile avec la fonctionnalité située sur le côté de la base de données. Heureusement, nous avons développé un ensemble important d'autotests couvrant des zones critiques.
Mais il n'y avait aucun test pour C # - par conséquent, tout a été vérifié à la main. Il s'agissait d'un travail considérable et la vérification a pris du temps.
Saut de foi - Déploiement productif pilote
Malgré les tests, le déploiement sur un produit pour la première fois était effrayant.
Nous avons eu de la chance - nous venions de planifier le prochain déploiement du système sur un nouveau site. Et nous avons décidé d'utiliser cette chance pour un déploiement pilote.
Les utilisateurs n'ont pas vu, les éventuelles erreurs du nouvel assemblage étaient simples à corriger, le vrai travail productif n'a pas encore commencé.
Nous avons déployé le système, et pendant plusieurs semaines il était en mode d'utilisation pré-productive (faible charge, un certain schéma d'utilisation qui peut être sauté dans le produit). Pendant ce temps, plusieurs défauts manquants lors des tests ont été révélés. Ils ont corrigé au fur et à mesure de leur découverte, et la nouvelle version a immédiatement été déployée pour vérification.
Après le lancement officiel et une semaine de soutien après le lancement, nous avons annoncé qu'il s'agissait du premier exemplaire assemblé et livré "d'une manière nouvelle".
Cette version de l'assemblage est devenue la première version stable de la branche principale, elle a été accrochée avec des balises fisrt_deployment (nous n'avons pas commandé d'icônes avec un hachage de la validation).
Déploiement à l'échelle sur un paysage productif complet
Comme James Bond l'a dit: "La deuxième fois, c'est beaucoup plus simple." Après le succès du déploiement pilote, nous avons rapidement connecté les instances restantes de systèmes de type similaire.
Mais le système a plusieurs types d'utilisation - une fonctionnalité peut être utilisée pour un type et non utilisée dans d'autres cas. En conséquence, la fonctionnalité testée sur la mise en œuvre du premier type ne garantissait pas nécessairement le succès pour d'autres cas.
Pour tester la fonctionnalité des types d'utilisation restants, nous avons commencé à utiliser des projets actifs en cours de développement. L'idée était similaire et le premier déploiement - nous avons commencé à utiliser des assemblages automatiques, en les «glissant» aux utilisateurs avec la fonctionnalité de conception. Ainsi, les utilisateurs, travaillant avec la version "projet" du produit, ont en même temps vérifié l'ancienne fonctionnalité.
La mise à l'échelle elle-même a révélé des problèmes techniques inattendus:
Paysage de système inhomogène
En plus de déployer directement l'application, nous devions d'abord nous assurer que tout était identique partout - versions .Net, Powershell et modules. Tout cela a pris pas mal de temps.
Connexion réseau
Sur certains sites, la connexion réseau ne permettait tout simplement pas de pomper tous les composants de l'ensemble. Il y a eu des délais d'attente, des dégâts pendant le transfert. Nous avons vérifié et essayé beaucoup de choses - pas très bien.
Je devais m'attarder sur la solution suivante: le script de construction a été finalisé afin que tous les résultats soient regroupés dans une grande archive, qui a ensuite été coupée en petits fragments (2 Mo chacun). Nous avons finalisé le scénario de déploiement pour éliminer la concurrence lors du téléchargement des artefacts, accepté les 2 fragments de 2 mégaoctets et restauré à partir d'eux ce qui peut déjà être étendu.
Conflit avec un antivirus
Un autre problème étrange que nous avons rencontré est un conflit entre un logiciel antivirus et l'une des étapes de déploiement: lorsque toutes sortes de fichiers "suspects", tels que .js, .dll, sont extraits des archives d'artefacts, l'antivirus commence à les examiner de près. Et le plus étrange est que l'antivirus commence à se précipiter sur le fichier avant la fin du déballage et que le processus de déballage tombe avec le message «le fichier est occupé par un autre processus». Bien que nous ayons du mal à cela, à l'exclusion du dossier local contenant des artefacts de l'analyse, ce n'est pas très bon, mais nous n'avons rien trouvé d'autre.
Amélioration des processus
, " " — .
- (service-now.com) VSTS Work Items. develop — .
- CI feature . —
- "self-service"
- — . , .
- : , CI/CD ,
- ( )
- - ( ) . — , .
- VSTS , , .
Résumé
- MS VisualStudio Team Services ( — Azure Devops) . — GIT
- (/)
- git / GitFlow .
- code review .
- CI. , feature , .
- . , .
- ( ) — . - .
- - 1 . - .
- "" .
— 14
, , , .
, — 250 * .