17 points par GN⁺ 2024-08-04 | 3 commentaires | Partager sur WhatsApp

« La table merchants2 ? Oui, nous l’avons créée parce qu’il manquait des colonnes dans merchants. »

  • Quand j’ai commencé la programmation, je ne savais même pas que des gens gagnaient leur vie en programmant
  • J’ai énormément appris dans mon premier poste dans le logiciel, mais la base de code de cet endroit était à la fois la pire et la meilleure

Les bases de données survivent à tout

  • Dans un système legacy, la base de données joue un rôle qui dépasse largement le simple stockage des données. Elle définit les contraintes de l’ensemble du système et constitue le point de rencontre de tout le code
  • SQL Server impose une limite au nombre de colonnes par table. À l’époque, c’était 1024, aujourd’hui 4096. Comme il n’y avait plus assez de colonnes dans la table Merchants, une table Merchants2 a été créée (avec plus de 500 colonnes)
  • Merchants et Merchants2 étaient au cœur du système. Tout était relié à Merchants. Il existait aussi d’autres tables normalisées, mais elles avaient des relations de clé étrangère avec Merchants

SequenceKey

  • SequenceKey est une table très simple avec une seule colonne et une seule valeur
  • Elle servait à générer des identifiants. On suppose qu’elle a été créée parce que SQL Server ne prenait pas en charge les ID auto-incrémentés
  • Toutes les procédures stockées récupéraient une clé dans SequenceKey, l’incrémentaient, puis l’utilisaient comme ID dans plusieurs tables. Elle servait ainsi de jointure implicite

Calendar

  • Calendar est une table de calendrier remplie manuellement. Quand Calendar expirait, il devenait impossible de se connecter au système
  • Cela s’est déjà produit il y a quelques années, et un stagiaire a ajouté 5 années supplémentaires. Personne ne sait quels systèmes l’utilisent

Employees

  • Tous les matins à 7 h 15, la table employees était supprimée puis remplie à nouveau à partir d’un CSV reçu d’ADP
  • Pendant cette opération, il était impossible de se connecter au système. Et parfois, ce traitement échouait
  • Comme les données devaient être répliquées vers le siège, elles étaient envoyées par email à une personne qui appuyait chaque jour sur un bouton pour les copier

La base de données de remplacement

  • On pourrait penser qu’il devait être possible de remettre de l’ordre dans la base de données. L’entreprise l’a pensé aussi
  • Il existait une copie de la base de données, avec environ 10 minutes de retard sur les données. La synchronisation ne se faisait que dans un seul sens
  • Cette base était normalisée, donc pour retrouver un numéro de téléphone dans merchants, il fallait faire 7 jointures

Les performances commerciales

  • Tous les commerciaux avaient chaque mois un quota appelé « win » à atteindre
  • La table qui gérait cela était extrêmement complexe. Une tâche s’exécutait chaque jour pour trouver les lignes ajoutées ou modifiées et les synchroniser avec le système du siège
  • Un problème est apparu quand un commercial a demandé qu’un enregistrement soit modifié manuellement
  • Ce commercial avait déjà atteint son quota de win et réalisé une grosse vente supplémentaire ce mois-là, mais il voulait la déplacer au mois suivant
  • Un stagiaire a pris en charge ce travail, puis la rumeur s’est propagée, et les demandes ont augmenté de façon exponentielle pendant 3 ans
  • À un moment, il y avait 3 stagiaires dont le travail consistait uniquement à écrire des requêtes SQL. Créer une application pour cela était considéré comme trop difficile

La base de code

  • La première base de code que j’ai connue était sur Team Foundation Server, un système de gestion de versions centralisé
  • La base sur laquelle j’ai principalement travaillé était composée pour moitié de VB et pour moitié de C#. Elle tournait sur IIS et utilisait l’état de session absolument partout
  • En pratique, cela signifiait qu’en accédant à une page par le chemin A ou le chemin B, on pouvait voir des choses très différentes sur cette même page
  • Tous les frameworks JavaScript qui existaient à l’époque avaient été commités dans ce dépôt, généralement avec des modifications maison que l’auteur estimait nécessaires. On remarquait en particulier knockout, backbone, marionette, ainsi que jquery et des plugins jquery
  • En plus de cette base de code, il y avait une douzaine de services SOAP et plusieurs applications Windows natives

Le disque dur de Gilfoyle

  • Gilfoyle était réputé pour être un programmeur incroyablement rapide. Je ne l’ai jamais rencontré, mais j’ai appris à le connaître à travers son code et celui qui restait sur son disque dur
  • Munch gardait sur son bureau le disque dur de Gilfoyle dans une configuration RAID, alors même que Gilfoyle avait quitté l’entreprise depuis des années
  • La raison, c’est que Gilfoyle était connu pour ne pas commit son code et pour créer des applications Windows jetables et arbitraires pour un seul utilisateur
  • Il n’était pas rare qu’un utilisateur arrive avec un rapport de bug sur une application qui n’existait que sur le disque dur de Gilfoyle

Le bug d’expédition

  • L’essentiel de mon travail consistait à traquer les bugs que l’équipe ne voulait pas se voir attribuer
  • Il y avait un bug particulièrement pénible qui revenait tous les quelques mois : des commandes restaient bloquées dans la file d’expédition après l’envoi et apparaissaient comme non expédiées alors qu’elles l’étaient déjà
  • Pour le corriger, on a essayé plusieurs solutions de contournement (scripts SQL, applications Windows, etc.). On m’avait conseillé de ne pas chercher la cause racine, mais je n’ai pas pu m’en empêcher
  • Au passage, j’ai appris à penser comme Gilfoyle. L’application d’expédition chargeait l’intégralité de la base de données, puis filtrait par date afin de conserver toutes les commandes postérieures à la date de démarrage de l’application
  • L’application dépendait d’un service SOAP, non pas pour faire des choses de service, mais comme fonction pure. C’était le client qui provoquait tous les effets de bord
  • Dans ce client, j’ai découvert une énorme hiérarchie de classes : 120 classes, chacune avec diverses méthodes, avec un héritage descendant jusqu’à 10 niveaux
  • Le seul problème, c’est que toutes les méthodes étaient vides
  • Tout cela existait pour mettre en place une structure exploitable par réflexion. Cette réflexion construisait une chaîne délimitée par des barres verticales (de structure fondée sur la base de données, mais entièrement statique) et l’envoyait via un socket
  • Au final, c’était transmis à Kewill, un service qui communiquait avec les transporteurs. Le bug venait du fait que Kewill réutilisait chaque mois des numéros à 9 chiffres, et que quelqu’un avait désactivé la tâche cron qui supprimait les anciennes commandes

Un magnifique chaos

  • Il y aurait encore énormément à dire sur cette base de code : l’équipe de développeurs seniors qui a passé 5 ans à tout réécrire sans jamais livrer de code, ou les consultants Red Hat qui voulaient construire une base de données unique pour tout contrôler
  • Cette base de code avait de nombreux coins complètement fous, et bien des raisons qui auraient justifié d’avoir une équipe dédiée pour repartir de zéro sur une seule fonctionnalité
  • Pourtant, l’histoire la plus importante est celle de Justin améliorant la page Merchants Search. C’était le point d’entrée de toute l’application
  • Tous les agents du service client parlaient au téléphone avec des commerçants, saisissaient un ID ou un nom pour retrouver les informations, puis arrivaient sur une immense page contenant tout
  • Cette page était dense d’informations dans le meilleur sens du terme, remplie de tout ce qu’il fallait savoir et de tous les liens qu’on pouvait vouloir visiter. Mais elle était terriblement lente
  • Justin était le seul développeur senior de mon groupe. Il était intelligent, sarcastique et ne se souciait guère du business
  • Il disait les choses franchement, sans ménagement, et il pouvait toujours résoudre seul un problème plus vite que les équipes autour de lui
  • Un jour, Justin en a eu assez d’entendre à quel point la page de recherche des commerçants était lente, alors il est allé la corriger
  • Chaque bloc de l’écran est devenu son propre endpoint. Au chargement, tout ce qui était au-dessus de la ligne de flottaison commençait à être récupéré, puis une fois un élément chargé, d’autres requêtes partaient
  • Le temps de chargement de la page est passé de plusieurs minutes à moins d’une seconde

Deux façons de découpler

  • Si Justin a pu faire cela, c’est parce qu’il n’y avait pas de plan directeur dans cette base de code
  • Il n’y avait aucun schéma général auquel le système devait se conformer, aucun format d’API attendu, aucun design system documenté, aucun comité de revue d’architecture chargé de garantir la cohérence
  • L’application était un chaos total. Personne ne pouvait la réparer, donc personne n’essayait. À la place, nous construisions chacun notre petit monde rationnel
  • Cette application monolithique avait grandi, par pure nécessité, en un microcosme de petites applications utiles sur ses bords
  • Toute personne chargée d’améliorer une partie de l’application finissait inévitablement par abandonner l’idée de démêler cette toile d’araignée, trouvait un petit coin propice pour construire quelque chose de nouveau, puis mettait lentement à jour les liens pour pointer vers cette nouvelle chose plus saine, laissant l’ancienne à l’abandon
  • Cela peut sembler désordonné. Pourtant, c’était étonnamment agréable d’y travailler. Les inquiétudes sur la duplication de code disparaissaient, tout comme celles sur la cohérence ou sur la scalabilité
  • Le code était écrit pour être utilisé, en touchant le moins possible aux zones voisines, et de manière à pouvoir être remplacé facilement. Notre code était découplé, simplement parce que le couplage était plus difficile à mettre en place

Après ça

  • Depuis, je n’ai jamais eu le privilège de travailler dans une base de code aussi admirablement hideuse
  • Toutes les bases de code hideuses que j’ai croisées depuis n’ont pas réussi à dépasser ce besoin de cohérence
  • C’est peut-être parce que les développeurs « sérieux » avaient abandonné cette base de code depuis longtemps. Il ne restait plus que des stagiaires désordonnés et des développeurs juniors
  • Ou peut-être parce qu’il n’y avait pas de couche intermédiaire entre les développeurs et les utilisateurs. Pas de traduction, pas de collecte des besoins, pas de tickets. Juste vous debout devant le bureau d’un agent du service client à lui demander comment améliorer sa vie
  • Ce lien direct me manque. Le feedback rapide, l’absence de besoin d’élaborer de grands plans, le lien simple entre un problème et le code
  • C’est peut-être seulement de la nostalgie naïve. Mais de la même manière que j’ai parfois envie de retourner aux pires années de mon enfance, chaque fois que je me retrouve face à des « enterprise design patterns », mon esprit retourne vers cette base de code magnifique et terrible

L’avis de GN⁺

  • Cet article peut apporter de l’empathie et du réconfort aux développeurs qui travaillent sur des systèmes legacy. Il montre que même une base de code imparfaite peut avoir de la valeur et offrir beaucoup d’enseignements
  • Mais les problèmes de cette base de code ne doivent pas être romantisés. Quand la dette technique s’accumule, elle ralentit fortement le développement et rend la maintenance difficile. À long terme, cela coûte plus cher
  • Renoncer à améliorer la base de code et continuer à empiler les rustines n’est pas la bonne réponse. Il faut une stratégie pour améliorer progressivement un système legacy ou le migrer
  • La culture et les processus de développement comptent aussi. Bien appliquer les pratiques d’ingénierie comme la revue de code, la conception d’architecture et la documentation aide à construire de meilleures bases de code
  • Une communication étroite avec les utilisateurs et un feedback rapide sont de bonnes choses. L’idéal est de les encourager via des méthodologies comme l’agile tout en gardant la qualité du code sous contrôle
  • Au final, tout est une question d’équilibre. Plutôt que de viser la perfection, l’important est de répondre aux besoins des utilisateurs de façon durable tout en maîtrisant la dette technique

3 commentaires

 
bus710 2024-08-07

Dans mon premier emploi comme développeur firmware, la mission qu’on m’a confiée consistait à analyser le code assembleur extrait du hex d’un MCU 8051 d’un produit dont il n’y avait ni développeur ni code source, puis à le réimplémenter en C…
Heureusement, il y avait au moins un produit fonctionnel, donc en lisant le code puis en testant le produit, j’ai tant bien que mal réussi à m’en sortir…
J’ai même eu droit à des menaces du genre : soit je le réparais pendant un déplacement en province, soit je repartais après m’être coupé un doigt.
Il y a aussi eu un cas où un bug incompréhensible venait en réalité de l’ascenseur situé derrière le mur où l’appareil était installé,
et je me souviens aussi être entré sur le site de conférence APEC de Dongbaekseom à Busan avant son ouverture officielle pour y installer diverses choses, haha

 
GN⁺ 2024-08-04
Avis sur Hacker News
  • Dans sa première entreprise, il a maintenu une application VB complexe

    • Il y avait beaucoup de variables globales adaptées aux besoins de chaque client
    • Comme les bugs n’apparaissaient pas en mode debug, il installait Visual Studio chez les clients et leur apprenait à lancer l’application en mode debug
    • Il n’y avait pas de gestion de versions, et le code était copié dans plusieurs dossiers, ce qui créait de la confusion
    • En cas de problème chez un client, il modifiait le code directement sur place
    • Comme personne ne se mettait d’accord sur une version finale, chaque client utilisait une version différente
  • Dans son premier emploi, il a assuré la maintenance d’un produit legacy écrit en COBOL et Java

    • Il travaillait en faisant des check-out de fichiers individuellement dans le système de contrôle de source
    • Chaque client avait un fichier jar « maître » représentant le produit finalement compilé
    • Après modification du code, il exécutait un script pour mettre à jour le fichier jar maître
    • La base de code ne compilait pas dans son ensemble et était patchée manuellement
    • Cela avait généré de nombreuses incohérences dans la base de code
    • La migration vers git a pris 2 ans
  • Il a refactoré un script Perl de plus de 12 000 lignes

    • Son auteur ne connaissait pas les tableaux et les avait donc implémentés avec des chaînes de caractères
    • Après refactorisation, le code est passé à 200 lignes
  • Il a constaté l’écart entre la théorie et la pratique

    • Beaucoup d’entreprises et de projets suivent un parcours similaire
    • On discute des méthodes idéales, mais en réalité on avance avec ce qui fonctionne
  • La base de code du client Android de Telegram était extrêmement complexe

    • À cause de la taille des fichiers, GitHub renonçait à les afficher
    • Une classe unique gérait tout le rendu et toutes les interactions des messages
    • Il avait obtenu l’autorisation de refactorer, mais n’a pas pu le faire
  • Dans son premier emploi, il a résolu un problème de mémoire dans un traitement de reporting

    • Son chef d’équipe et son supérieur ont été surpris
    • Les autres développeurs détestaient Linux et voulaient passer à .NET
  • Il a apprécié l’expérience consistant à résoudre les problèmes en communiquant directement avec les clients

    • Il créait rapidement des prototypes et les faisait tester par les clients
    • La base de code était désordonnée, mais elle fonctionnait bien
  • Il a créé un système prenant en charge plusieurs marchés

    • La version internationale a été créée en copiant le système d’origine
    • Cinq ans plus tard, les systèmes n’étaient plus séparés mais complètement entremêlés
    • Le nouveau système et l’ancien sont étroitement couplés
  • Dans son premier emploi, beaucoup de gens manquaient d’expérience

    • Après avoir gagné en expérience, il est parti vers un meilleur poste
  • Il a expliqué comment résoudre des problèmes dans SQL Server moderne

    • Personnalisation client, ID partagés, tables calendrier manuelles, partitionnement de tables, répliques de reporting différées, etc.
    • Les bases de code mixtes VB et C# sont courantes
    • Il est possible d’utiliser des outils de conversion automatique
 
reddiana 2024-08-05

La table de numérotation (SequenceKey) et la table des jours ouvrés (Calendar)
ça me rappelle des souvenirs. Je ne sais pas comment on fait maintenant, mais autrefois c’étaient des tables couramment utilisées. En faisant du SI, l’équipe en charge des fonctions communes du métier implémentait les fonctionnalités associées.