web-dev-qa-db-fra.com

Maintenir des centaines de succursales personnalisées sur la branche principale

Actuellement, nous avons une branche principale pour notre application PHP dans un référentiel partagé. Nous avons plus de 500 clients abonnés à notre logiciel, dont la plupart ont une personnalisation à des fins différentes, chacun dans un La personnalisation peut être un nom de champ de texte différent, une fonctionnalité ou un module totalement nouveau ou de nouvelles tables/colonnes dans la base de données.

Le défi auquel nous sommes confrontés est que lorsque nous maintenons ces centaines de branches personnalisées et distribuons aux clients, nous fournissons de temps en temps de nouvelles fonctionnalités et mettons à jour notre branche principale, et nous aimerions pousser les modifications de la branche principale vers les branches personnalisées afin de mettre à jour les à la dernière version.

Malheureusement, cela entraîne souvent de nombreux conflits dans le code personnalisé, et nous passons de nombreuses heures à parcourir chaque branche pour résoudre tous les conflits. C'est très inefficace, et nous avons constaté que les erreurs ne sont pas rares lors de la résolution de ces conflits.

Je suis à la recherche d'un moyen plus efficace de garder nos succursales de mise à jour client à jour avec la branche principale, ce qui entraînera moins d'efforts lors de la fusion.

145
Fernando Tan

Vous abusez complètement des branches! Vous devriez avoir la personnalisation alimentée par la flexibilité dans votre application, et non la flexibilité dans votre contrôle de version (qui, comme vous l'avez découvert, n'est pas destiné/conçu pour ce type d'utilisation).

Par exemple, faites en sorte que les étiquettes de champ de texte proviennent d'un fichier texte, ne soient pas codées en dur dans votre application (c'est ainsi que fonctionne l'internationalisation). Si certains clients ont des fonctionnalités différentes, faites votre application modulaire, avec des limites internes strictes régies par des API rigoureuses et stables, afin que les fonctionnalités puissent être connectées selon les besoins.

L'infrastructure de base et toutes les fonctionnalités partagées n'ont alors besoin que d'être stockées, maintenues et testées ne fois.

Vous auriez dû le faire dès le départ. Si vous avez déjà cinq cents variantes de produits (!), Réparer cela va être une tâche énorme… mais pas plus que la maintenance continue.

Avoir 500 clients est un problème agréable, si vous aviez passé du temps à éviter ce problème avec les succursales, vous n'auriez peut-être jamais pu continuer à négocier assez longtemps pour obtenir des clients.

Tout d'abord, j'espère que vous facturerez suffisamment à vos clients pour couvrir TOUS les coûts de maintenance de leurs versions personnalisées. Je suppose que les clients s'attendent à obtenir de nouvelles versions sans avoir à payer pour que leurs personnalisations soient à nouveau effectuées. Je commencerais par trouver tous les fichiers identiques dans 95% de vos succursales. Ce 95% est la partie stable de votre application.

Ensuite, trouvez tous les fichiers qui n'ont que quelques lignes différentes entre les branches - essayez d'introduire un système de configuration de sorte que ces différences puissent être supprimées. Ainsi, par exemple, plutôt que d'avoir des centaines de fichiers avec des étiquettes de champ de texte différentes, vous avez 1 fichier de configuration qui peut remplacer n'importe quelle étiquette de texte. (Cela ne doit pas être fait en une seule fois, il suffit de configurer une étiquette de champ de texte configurable la première fois qu'un client souhaite la modifier.)

Passez ensuite aux problèmes les plus difficiles en utilisant le modèle de stratégie, l'injection de dépendance, etc.

Pensez à stocker json dans la base de données plutôt qu'à ajouter des colonnes pour les propres champs du client - cela peut fonctionner pour vous si vous n'avez pas besoin de rechercher ces champs avec SQL.

Chaque fois que vous archivez un fichier dans une branche, vous DEVEZ le différencier avec principal et justifier chaque modification, y compris les espaces blancs. De nombreux changements ne seront pas nécessaires et peuvent être supprimés avant l'enregistrement. Cela peut être dû au fait qu'un développeur a des paramètres différents dans son éditeur pour la façon dont le code est formaté.

Vous visez d'abord à passer de 500 branches avec beaucoup de fichiers différents à la plupart des branches ayant seulement quelques fichiers différents. tout en gagnant suffisamment d'argent pour vivre.

Il se peut que vous ayez encore 500 succursales dans de nombreuses années, mais si elles sont beaucoup plus faciles à gérer, alors vous avez gagné.


Sur la base du commentaire de br3w5:

  • Vous pouvez suivre chaque classe différente d'un client à l'autre
  • Faire un "xxx_baseclass" qui définit toutes les méthodes qui sont appelées dans la classe de l'extérieur
  • Renommez la classe pour que xxx s'appelle xxx_clientName (en tant que sous-classe de xxx_baseclass)
  • Utiliser l'injection de dépendances pour que la version correcte de la classe soit utilisée pour chaque client
  • Et maintenant, pour l'intelligence intelligente br3w5 est venu avec! Utilisez un outil d'analyse de code statique pour trouver le code désormais dupliqué et déplacez-le dans la classe de base, etc.

Ne faites ce qui précède qu'après avoir obtenu le grain facile et commencez par le suivre avec quelques classes.

93
Ian

À l'avenir, posez le test Joel questions dans votre entretien. Vous seriez plus susceptible de ne pas entrer dans un naufrage.


C'est un, ah, comment dirons-nous ... vraiment, vraiment mauvais problème à avoir. Le "taux d'intérêt" sur cette dette technique va être très, très élevé. Il pourrait ne pas être récupérable ...

Dans quelle mesure ces modifications personnalisées sont-elles intégrées au "noyau"? Pouvez-vous en faire leur propre bibliothèque et avoir un seul "noyau" et chaque client spécifique ayant son propre "module complémentaire"?

Ou s'agit-il toutes de configurations très mineures?

Je pense que la solution est une combinaison de:

  • Changer toutes les modifications codées en dur en éléments basés sur la configuration. Dans ce cas, tout le monde a la même application principale, mais les utilisateurs (ou vous) activent/désactivent la fonctionnalité, définissent les noms, etc. selon les besoins
  • Déplacer des fonctionnalités/modules "spécifiques au client" vers des projets séparés, donc au lieu d'avoir un "projet", vous avez un "projet principal" avec des modules que vous pouvez ajouter/supprimer facilement. Alternativement, vous pouvez également effectuer ces options de configuration.

Ni l'un ni l'autre ne sera trivial car si vous vous retrouvez ici avec plus de 500 clients, vous n'avez probablement fait aucune distinction réelle à ce sujet. Je m'attends à ce que vos changements dans la séparation de cette tâche soient très longs.

Je soupçonne également que vous allez avoir des problèmes importants pour séparer et classer facilement tout votre code spécifique au client.

Si la plupart de vos changements sont spécifiquement des différences de formulation, je vous suggère de lire des questions comme this sur la localisation de la langue. Que vous fassiez entièrement plusieurs langues ou simplement un sous-ensemble, la solution est la même. This est spécifiquement PHP et localisation.

39
enderland

C'est l'un des pires anti-patterns que vous pouvez atteindre avec n'importe quel VCS.

L'approche correcte ici consiste à transformer le code personnalisé en quelque chose piloté par la configuration, puis chaque client peut avoir sa propre configuration, soit codée en dur dans un fichier de configuration, soit dans une base de données ou un autre emplacement. Vous pouvez activer ou désactiver des fonctionnalités entières, personnaliser l'apparence des réponses, etc.

Cela vous permet de conserver une branche principale avec votre code de production.

17
Daenyth

Le but des branches est d'explorer une voie possible de développement sans risquer de casser la stabilité de la branche principale. Ils devraient finalement être fusionnés à un moment approprié, ou être jetés s'ils conduisent à une impasse. Ce que vous avez n'est pas tellement de branches, mais bien plutôt 500 fourches du même projet et essayer d'appliquer les changements vitaux à toutes est un sisyphe tâche.

Ce que vous devriez faire à la place, c'est d'avoir votre code principal en direct dans son propre référentiel, avec les points d'entrée nécessaires pour modifier le comportement via la configuration et pour injecter le comportement autorisé par dépendances inversées .

Les différentes configurations que vous avez pour les clients peuvent alors simplement se distinguer les unes des autres par un état configuré en externe (par exemple une base de données) ou, si nécessaire, vivre en tant que référentiels distincts, qui ajoutent le noyau en tant que sous-module.

13
back2dos

Toutes les choses importantes ont été proposées par de bonnes réponses ici. Je voudrais ajouter mes cinq sous comme suggestion de processus.

Je voudrais vous suggérer de résoudre ce problème à long ou moyen terme et adopter votre politique, comment vous développez le code. Essayez de devenir une équipe d'apprentissage flexible. Si quelqu'un a permis d'avoir 500 repos au lieu de rendre le logiciel configurable, alors il est temps de vous demander comment vous avez travaillé jusqu'à présent et vous ferez désormais.

Ce qui signifie:

  1. Clarifier les responsabilités de gestion du changement: si un client a besoin de quelques adaptations, qui les vend, qui les autorise et qui décide comment le code sera être changé? Où sont les vis à tourner si certaines choses doivent être modifiées?
  2. Clarifiez le rôle, qui dans votre équipe est autorisé à faire de nouveaux dépôts, et qui ne l'est pas.
  3. Essayez de vous assurer que tout le monde dans votre équipe voit la nécessité de modèles qui permettent la flexibilité du logiciel.
  4. Clarifiez votre outil de gestion: comment savoir rapidement quel client a quelles adoptions de code. Je sais, une "liste de 500" semble ennuyeux, mais voici une "économie émotionnelle", si vous voulez. Si vous ne pouvez pas voir les changements du client en un clin d'œil, vous vous sentez encore plus perdu et attiré comme si vous deviez commencer une liste. Ensuite, utilisez cette liste pour regrouper les fonctionnalités de la façon dont les réponses des autres ici vous ont montré:
    • regrouper les clients par changements mineurs/majeurs
    • regrouper par sujet les changements liés
    • regrouper par changements faciles à fusionner et changements difficiles à fusionner
    • trouver des groupes de modifications égales apportées à plusieurs dépôts (oh oui, il y en aura).
    • peut-être le plus important pour parler à votre gestionnaire/investisseur: grouper par cher changements et pas cher changements.

Ce n'est en aucun cas destiné à créer une mauvaise atmosphère de pression dans votre équipe. Je vous suggère plutôt de clarifier ces points d'abord par vous-même et, partout où vous ressentez le soutien, d'organiser cela avec votre équipe. Invitez des personnes amicales à la table afin d'améliorer toute votre expérience.

Ensuite, essayez d'établir une fenêtre de temps à long terme, où vous cuisinez cette chose sur une petite flamme. Suggestion: essayez de fusionner au moins deux dépôts chaque semaine, et ainsi en supprimer au moins un. Vous pouvez apprendre que souvent, vous pouvez fusionner plus de deux succursales, à mesure que vous obtenez la routine et la surveillance. De cette façon, en un an, vous pouvez traiter les pires branches (les plus chères?), Et en deux ans, vous pouvez réduire ce problème pour avoir un logiciel nettement meilleur. Mais ne vous attendez pas à plus, car au final personne n'aura "le temps" pour cela, mais vous êtes celui qui ne le permettra plus car vous êtes l'architecte logiciel.

C'est ainsi que j'essaierais de le gérer si j'étais à votre place. Cependant, je ne sais pas comment votre équipe va accepter de telles choses, comment le logiciel permet vraiment cela, comment vous êtes pris en charge et aussi ce que vous avez encore à apprendre. Vous êtes l'architecte logiciel - allez-y :-)

7
peter_the_oak

Contrastant tous les opposants, supposons un réel besoin commercial.

(par exemple, le livrable est le code source, les clients sont du même secteur d'activité et donc concurrents les uns des autres, et votre modèle commercial promet de garder ses secrets secrets)

En outre, supposons que votre entreprise dispose des outils nécessaires pour maintenir toutes les succursales, c'est-à-dire soit de la main-d'œuvre (disons 100 développeurs dédiée à la fusion, en supposant un délai de publication de 5 jours; soit 10 développeurs en supposant Délai de publication de 50 jours est OK), ou de tels tests automatisés impressionnants que les fusions automatisées sont vraiment testées à la fois pour spécification principale et spécification d'extension dans chaque branche, et ainsi, seuls les changements qui ne fusionnent pas "proprement" nécessitent une intervention humaine. Si vos clients paient non seulement pour des personnalisations mais pour leur maintenance, cela peut être un modèle commercial valide.

Ma question (et non) est la suivante: avez-vous un personne dédiée responsable de la livraison à chaque client? Si vous êtes, disons, une entreprise de 10 000 personnes, ce peut être le cas.

Cela pourrait être géré par architecture du plugin dans certains cas, disons que votre cœur est le tronc, les plugins pourraient être conservés dans le tronc ou les branches, et la configuration pour chaque client est soit un fichier de nom unique, soit conservé dans le client branche.

Les plugins peuvent être chargés au moment de l'exécution ou intégrés au moment de la compilation.

Vraiment, de nombreux projets sont réalisés de cette manière, fondamentalement le même problème s'applique toujours - les changements de base simples sont triviaux à intégrer, les changements de conflit doivent être annulés ou des changements sont nécessaires pour de nombreux plugins.

Il y a des cas où les plugins ne sont pas assez bons, c'est quand tant de internes du noyau doivent être ajustés que le nombre d'interfaces de plugins devient trop grand pour être géré.

Idéalement, cela serait géré par programmation orientée aspect, où trunk est le code principal et les branches sont des aspects (c'est-à-dire du code supplémentaire et des instructions sur la façon de connecter les extras au noyau)

Un exemple simple, vous pouvez spécifier que foo personnalisé est exécuté avant ou après le noyau klass.foo ou qu'il le remplace, ou qu'il l'enveloppe et puisse changer d'entrée ou de sortie.

Il y a une tonne de bibliothèques pour cela, mais le problème des conflits de fusion ne disparaît pas - les fusions propres sont gérées par AOP et les conflits nécessitent toujours une intervention humaine.

Enfin, une telle entreprise doit vraiment se préoccuper de maintenance de la succursale, à savoir, la fonctionnalité spécifique au client X est-elle si courante qu'il est moins coûteux de la déplacer vers le cœur, même si tous les clients ne la paient pas?

5
Dima Tisnek

Vous ne résolvez pas la cause profonde de la maladie en regardant le symptôme. L'utilisation d'une approche de "gestion de code" est symptomatique, mais ne résoudra pas les choses à long terme. La cause première est le manque de capacités, de fonctionnalités et d'extensions et de variantes de produits "bien gérés".

Votre code "personnalisé" ne représente rien d'autre que des extensions des caractéristiques et capacités du produit et champ de données modifications sur d'autres.

L'étendue des fonctionnalités personnalisées, leur différence, leur similitude contextuelle ou non joueront beaucoup dans la "désinfection" de la base de code de votre produit.

Plus que la façon dont vous codez et versionnez, c'est un endroit où la gestion des produits, l'architecture des produits et l'architecture des données entrent en jeu. Sérieusement.

Parce que, en fin de compte, le code n'est rien d'autre que votre offre de fonctionnalités/services commerciaux et de produits à vos clients. C'est pour cela que votre entreprise est rémunérée.

Mieux gérer cela doit venir du point de vue des "capacités" et non du point de vue du code.

Vous, votre entreprise et votre produit ne pouvez pas tout faire pour tout le monde. Maintenant que vous avez une base de revenus décente de 500 clients, il est temps de produire ce que vous avez l'intention d'être.

Et si vous proposez plusieurs choses, il serait judicieux de modulariser les capacités de vos produits de manière organisée.

Quelle sera l'étendue et la profondeur de vos produits? Sinon, cela entraînera des problèmes de "qualité de service" et de "dilution et fragmentation des produits" au fur et à mesure.

Serez-vous un CRM ou ERP ou le traitement/expédition des commandes ou Microsoft Excel?

Vos extensions existantes doivent être regroupées et harmonisées, comme un grand logiciel majeur tire et fusionne produits acquis auprès d'une startup.

Vous devrez disposer d'une solide gestion des produits et architecture de données personne mapper les éléments suivants:

  • Branche principale, capacités de ses produits et base de fonctionnalités
  • Fonctionnalités, types et variantes d'extensions personnalisées
  • Importance et variation des "champs personnalisés"

..pour créer une feuille de route d'assimilation et d'harmonisation de tous ces fils/branches de produits en vrac dans le grand contexte de votre application principale.

PS: Connectez-vous avec moi, je connais une personne qui peut vous aider à résoudre ce problème :)

3
Alex S