Quand séparer un projet dans plusieurs sous-projets

30

Je voudrais savoir s'il est judicieux de diviser le projet sur lequel je travaille en deux référentiels au lieu d'un.

D'après ce que je peux dire:

  • Le frontend sera écrit en html + js
  • Backend en .net
  • Le backend ne dépend pas du frontend et le frontend ne dépend pas du backend
  • Le frontend utilisera une api reposante implémentée dans le backend.
  • Le frontend peut être hébergé sur n'importe quel serveur http statique.

Pour l'instant, le référentiel a cette structure:

racine:

  • l'extrémité avant/*
  • backend / *

Je pense que c'est une erreur de garder les deux projets dans le même référentiel. Étant donné que les deux projets n'ont pas de dépendances entre eux, ils doivent appartenir à des référentiels individuels et, si nécessaire, à un référentiel parent contenant des sous-modules.

On m'a dit que c'était inutile et que nous n'en tirerions aucun avantage.

Voici certains de mes arguments:

  • Nous avons deux modules qui ne dépendent pas l'un de l'autre.
  • A long terme, l'historique des sources des deux projets peut compliquer les choses (essayez de rechercher dans l'histoire quelque chose dans le frontend pendant que vous avez la moitié des validations qui ne sont absolument pas liées au bogue que vous recherchez)
  • Conflit et fusion (cela ne devrait pas se produire, mais le fait de demander à quelqu'un de pousser vers le backend forcera les autres développeurs à effectuer des changements de backend pour pousser les changements de frontend.)
  • Un développeur peut travailler uniquement sur le backend mais devra toujours tirer le frontend ou l'inverse.
  • À long terme, quand il sera temps de se déployer. D'une certaine manière, le frontend pourrait être déployé sur plusieurs serveurs statiques tout en ayant un seul serveur backend. Dans tous les cas, les utilisateurs seront obligés soit de cloner l'intégralité du backend avec, soit de créer un script personnalisé pour envoyer uniquement à tous les serveurs le frontend ou pour supprimer le backend. Plus facile de simplement pousser / tirer uniquement le frontend ou le backend que les deux si un seul est nécessaire.
  • Contre-argument (une personne peut travailler sur les deux projets), créer un troisième référentiel avec sous-module et développer avec lui. L'historique est conservé séparément dans les modules individuels et vous pouvez toujours créer des balises où la version du backend / frontend fonctionne vraiment ensemble en synchronisation. Avoir les deux frontend / backend ensemble dans un même dépôt ne signifie pas qu'ils fonctionneront ensemble. C'est juste fusionner les deux histoire en un seul grand repo.
  • Avoir frontend / backend comme sous-modules facilitera les choses si vous souhaitez ajouter un pigiste au projet. Dans certains cas, vous ne voulez pas vraiment donner un accès complet à la base de code. Avoir un gros module rendra les choses plus difficiles si vous voulez restreindre ce que les "étrangers" peuvent voir / modifier.
  • Introduction et correction de bug, j'ai inséré un nouveau bug dans le frontend. Ensuite, quelqu'un corrige un bug dans le backend. Avec un référentiel, la restauration avant le nouveau bogue entraînera également la restauration du backend, ce qui pourrait rendre sa correction difficile. Je devrais cloner le backend dans un dossier différent pour que le backend fonctionne tout en corrigeant le bogue dans le frontend ... puis en essayant de remettre les choses au point ... Avoir deux référentiels sera indolore car déplacer le HEAD d'un repo a gagné ne change pas l'autre. Et les tests avec différentes versions de backend seront indolores.

Quelqu'un peut-il me donner plus d'arguments pour les convaincre ou au moins me dire pourquoi il est inutile (plus compliqué) de diviser le projet en deux sous-modules. Le projet est nouveau et la base de code date de quelques jours, il n'est donc pas trop tôt pour le réparer.

Loïc Faure-Lacroix
la source

Réponses:

23

Dans mon entreprise, nous utilisons un référentiel SVN distinct pour chaque composant du système. Je peux vous dire que cela devient extrêmement frustrant. Notre processus de construction a tellement de couches d'abstraction.

Nous le faisons avec Java, nous avons donc un processus de construction lourd avec la compilation javac, la compilation de liaison JibX, la validation XML, etc.

Pour votre site, ce ne sera peut-être pas un gros problème si vous ne le "construisez" pas vraiment (comme le PHP vanille).

Inconvénients de diviser un produit en plusieurs référentiels

  1. Gestion de build - Je ne peux pas simplement extraire du code, exécuter un script de build autonome et avoir un produit exécutable / installable / déployable. J'ai besoin d'un système de build externe qui va vers plusieurs dépôts, exécute plusieurs scripts de build internes, puis assemble les artefacts.
  2. Suivi des changements - Voir qui a changé quoi, quand et pourquoi. Si une correction de bogue dans le frontend nécessite un changement de backend, il y a maintenant 2 chemins divergents pour que je puisse y revenir plus tard.
  3. Administration - voulez-vous vraiment doubler le nombre de comptes d'utilisateurs, de stratégies de mot de passe, etc. qui doivent être gérés?
  4. Fusion - Les nouvelles fonctionnalités sont susceptibles de changer beaucoup de code. En divisant votre projet en plusieurs référentiels, vous multipliez le nombre de fusions nécessaires.
  5. Création de branche - Même chose avec la branche, pour créer une branche, vous devez maintenant créer une branche dans chaque référentiel.
  6. Balisage - après un test réussi de votre code, vous souhaitez baliser une version pour publication. Vous avez maintenant plusieurs balises à créer, une dans chaque référentiel.
  7. Difficile de trouver quelque chose - Peut-être que le frontend / backend est simple, mais cela devient une pente glissante. Si vous vous divisez en suffisamment de modules, les développeurs devront peut-être rechercher où se trouve un morceau de code dans le contrôle de code source.

Mon cas est un peu extrême car notre produit est divisé en 14 référentiels différents et chaque dépôt est ensuite divisé en 4-8 modules. Si je me souviens bien, nous avons quelque part quelque 80 ou quelques "packages" qui doivent tous être vérifiés individuellement puis assemblés.

Votre cas avec juste backend / frontend peut être moins compliqué, mais je le déconseille toujours.

Des exemples extrêmes peuvent être des arguments convaincants pour ou contre à peu près n'importe quoi :)

Critères que j'utiliserais pour décider

J'envisagerais de diviser un produit en plusieurs référentiels de code source après avoir pris en compte les facteurs suivants:

  1. Build - Les résultats de la construction de chaque composant fusionnent-ils pour former un produit? Comme la combinaison de fichiers .class d'un tas de composants en une série de fichiers .jar ou .war.
  2. Déploiement - Vous retrouvez-vous avec des composants qui sont déployés ensemble comme une seule unité ou des unités différentes qui vont sur des serveurs différents? Par exemple, les scripts de base de données vont à votre serveur DB, tandis que javascript va à votre serveur Web.
  3. Co-changement - Ont-ils tendance à changer fréquemment ou ensemble? Dans votre cas, ils peuvent changer séparément, mais toujours fréquemment.
  4. Fréquence de ramification / fusion - si tout le monde vérifie dans le tronc et les branches sont rares, vous pourrez peut-être vous en tirer. Si vous vous branchez et fusionnez fréquemment, cela peut se transformer en cauchemar.
  5. Agilité - si vous avez besoin de développer, tester, publier et déployer un changement à tout moment (probablement avec SaaS), pouvez-vous le faire sans passer un temps précieux à jongler avec les branches et les repos?

Vos arguments

Je ne suis pas non plus d'accord avec la plupart de vos arguments pour cette scission. Je ne les contesterai pas tous parce que cette longue réponse deviendra encore plus longue, mais quelques-uns qui se démarquent:

Nous avons deux modules qui ne dépendent pas l'un de l'autre.

Absurdité. Si vous enlevez votre backend, votre frontend fonctionnera-t-il? C'est ce que je pensais.

A long terme, l'historique des sources des deux projets peut compliquer les choses (essayez de rechercher dans l'histoire quelque chose dans le frontend pendant que vous avez la moitié des validations qui ne sont absolument pas liées au bogue que vous recherchez)

Si la racine de votre projet est divisée en frontend / et backend /, vous pouvez consulter l'historique de ces hiérarchies indépendamment.

Conflit et fusion (cela ne devrait pas se produire, mais le fait de pousser quelqu'un vers le backend forcera un autre développeur à tirer les modifications du backend pour pousser les changements du frontend.) Un développeur peut travailler uniquement sur le backend mais devra toujours tirer le backend ou dans l'autre sens environ.

La division de votre projet en différents dépôts ne résout pas ce problème. Un conflit frontal et un conflit principal vous laisse toujours avec 2 conflits, que ce soit 1 référentiel fois 2 conflits ou 2 référentiels fois 1 conflit. Quelqu'un doit encore les résoudre.

Si le souci est que 2 repos signifie qu'un développeur frontal peut fusionner le code frontal tandis qu'un développeur principal fusionne le code principal, vous pouvez toujours le faire avec un seul référentiel utilisant SVN. SVN peut fusionner à n'importe quel niveau. Peut-être que c'est une limitation git ou mercurial (vous avez tagué les deux, donc vous ne savez pas quel SCM vous utilisez)?

D'autre part

Cela dit, j'ai vu des cas où le fractionnement d'un projet en plusieurs modules ou référentiels fonctionne. Je l'ai même préconisé une fois pour un projet particulier où nous avons intégré Solr dans notre produit. Bien sûr, Solr s'exécute sur des serveurs distincts, ne change que lorsqu'un ensemble de modifications est lié à la recherche (notre produit fait bien plus que la recherche), a un processus de génération distinct et il n'y a aucun artefact de code ou artefact de construction partagé.

Brandon
la source
La modération en toutes choses, comme disait ma mère ...
William Payne
Au moment où j'écris, j'écris le frontend sans backend. J'émule le backend avec des fichiers json, et je pourrais probablement même émuler complètement avec indexedDB dans le navigateur. Donc oui, le backend est juste un serveur desservant json. Il pourrait être remplacé par n'importe quoi tant que les données reçues sont conformes à l'API. Les deux projets utilisent un système de construction différent. En bref, c'est un peu comme avoir un site Web et une application Android mobile. Ajout de l'application mobile dans le référentiel du serveur Web.
Loïc Faure-Lacroix
De plus, si ce n'était pas clair, le backend et le frontend ne sont pas des interfaces utilisateur / administrateur. Mais le frontend n'est qu'une interface ajax et le backend sert json. Les utilisateurs et les rôles sont traités différemment et l'interface d'administration sera dans le frontend. L'idée est de garder les deux parties isolées et d'empêcher le HTML généré par Javascript de charger le HTML généré par le serveur. Le serveur ne doit servir que json ou xml.
Loïc Faure-Lacroix
1
Ensuite, vous n'avez aucun problème de construction ou de déploiement, donc cela peut être correct. Mais encore une fois, si vous effectuez un changement majeur, vous devrez peut-être changer l'API, ce qui affecte à la fois le frontend et le backend et donc vous allez vous ramifier deux fois, fusionner deux fois, marquer deux fois, etc. Mais tant qu'il ne reste que deux fois et ne ne se transforme pas en 3 ... 4 ... 12 ... 20, probablement pas une mauvaise idée.
Brandon
Même si l'API change, avec un bon versionning, il pourrait être possible de créer des versions de branche pour chaque frontend qui prend en charge une version d'API. Le backend devrait avoir une certaine compatibilité "en arrière" et garder l'ancienne API en fonctionnement aussi longtemps que possible.
Loïc Faure-Lacroix
3

Certains de vos arguments sont valides et d'autres non.

Nous avons deux modules qui ne dépendent pas l'un de l'autre.

Ce n'est en fait pas entièrement vrai. Pour pouvoir communiquer, le front-end et le back-end doivent avoir une interface commune (description). Cela en fait un argument faible en faveur des deux dans un référentiel commun. Mais seulement un argument faible car cela ne fait pas beaucoup de différence.

A long terme, l'historique des sources des deux projets peut compliquer les choses (essayez de rechercher dans l'histoire quelque chose dans le frontend pendant que vous avez la moitié des validations qui ne sont absolument pas liées au bogue que vous recherchez)

C'est un faux argument. Si vous voulez rechercher comment un bug particulier a été corrigé, vous recherchez dans le bug-tracker pour lequel commit contient le correctif. Et si vous voulez savoir comment un morceau de code particulier a évolué, vous regardez l'historique d'un seul fichier (ou tout au plus d'une poignée). Dans les deux cas, avoir d'autres fichiers, peut-être d'autres modules, dans le référentiel ne devrait en aucun cas compliquer les choses.

Conflit et fusion (cela ne devrait pas se produire, mais le fait de demander à quelqu'un de pousser vers le backend forcera les autres développeurs à effectuer des changements de backend pour pousser les changements de frontend.)

C'est un faux argument. Je ne connais aucun VCS (à moitié décent) où vous devez synchroniser l'ensemble du référentiel avant de pouvoir valider / pousser vos modifications. Tout au plus, vous devez synchroniser les dossiers contenant les fichiers modifiés (et souvent uniquement les fichiers eux-mêmes).

Un développeur peut travailler uniquement sur le backend mais devra toujours tirer le backend ou l'inverse.

Il s'agit du même faux argument que le précédent.

À long terme, quand il sera temps de se déployer. D'une certaine manière, le frontend pourrait être déployé sur plusieurs serveurs statiques tout en ayant un seul serveur backend. Dans tous les cas, les utilisateurs seront obligés soit de cloner l'intégralité du backend avec, soit de créer un script personnalisé pour envoyer uniquement à tous les serveurs le frontend ou pour supprimer le backend. Plus facile de simplement pousser / tirer uniquement le frontend ou le backend que les deux si un seul est nécessaire.

Selon la façon dont les gens envisagent le déploiement, cela peut être un argument valide. Si le déploiement se fera en décompressant un fichier zip / tarbal sur le serveur, alors peu importe la façon dont vos référentiels sont organisés. Si le déploiement se fait en extrayant (une partie) d'un référentiel sur le serveur, il peut être judicieux d'utiliser des référentiels distincts pour les modules qui sont déployés séparément.

Contre-argument (une personne peut travailler sur les deux projets), créer un troisième référentiel avec sous-module et développer avec lui. L'historique est conservé séparément dans les modules individuels et vous pouvez toujours créer des balises où la version du backend / frontend fonctionne vraiment ensemble en synchronisation. Avoir les deux frontend / backend ensemble dans un même dépôt ne signifie pas qu'ils fonctionneront ensemble. C'est juste fusionner les deux histoire en un seul grand repo.

C'est un argument valable, mais ce n'est pas si fort.

Avoir frontend / backend comme sous-modules facilitera les choses si vous souhaitez ajouter un pigiste au projet. Dans certains cas, vous ne voulez pas vraiment donner un accès complet à la base de code. Avoir un gros module rendra les choses plus difficiles si vous voulez restreindre ce que les "étrangers" peuvent voir / modifier.

Ceci est un argument valable.

Introduction et correction de bug, j'ai inséré un nouveau bug dans le frontend. Ensuite, quelqu'un corrige un bug dans le backend. Avec un référentiel, la restauration avant le nouveau bogue entraînera également la restauration du backend, ce qui pourrait rendre sa correction difficile.

C'est un argument bidon, car cela signifierait qu'après deux corrections de bogues sur un module, vous ne seriez pas en mesure de rétablir le premier. N'importe quel VCS à moitié décent vous permettra d'annuler à peu près n'importe quel ancien commit (bien que cela signifie souvent que vous effectuez un nouveau commit qui annule ces changements, parfois même pour le sommet de HEAD).

Je devrais cloner le backend dans un dossier différent pour que le backend fonctionne tout en corrigeant le bogue dans le frontend ... puis en essayant de remettre les choses au point ... Avoir deux référentiels sera indolore car déplacer le HEAD d'un repo a gagné ne change pas l'autre. Et les tests avec différentes versions de backend seront indolores.

C'est en fait un assez bon argument. Le fait d'avoir deux référentiels facilite le test des scénarios dans lesquels les frontaux et les backends déployés pourraient devenir (légèrement) désynchronisés.

Bart van Ingen Schenau
la source
Pour être honnête, la plupart des faux arguments peuvent être résolus avec des branches. Branche pour frontend et branche pour backend. Maître pour la synchronisation. Mais d'une certaine manière, gérer une branche comme celle-ci rend les choses plus compliquées que d'avoir deux dépôts.
Loïc Faure-Lacroix
1
@Sybiam: En fait, ce sont de faux arguments, car ils ne mettent pas en évidence un problème qui pourrait exister avec l'utilisation d'un référentiel unique, même si toutes les modifications ne sont apportées qu'à trunk / main.
Bart van Ingen Schenau
Je pense que vos critiques sont valables. Je ne pense tout simplement pas que c'était le but de la question.
sylvanaar
2

Ce post est un peu ancien mais j'aimerais y contribuer. Bien que votre serveur principal ne connaisse pas vraiment le serveur frontal, le serveur frontal doit avoir des demandes correspondant à l'API du serveur principal. Si vous considérez votre back-end comme une API REST, vous pouvez définir un fichier d'interface tel qu'une interface YAML swagger. Maintenant, il y a vraiment 3 projets, que vous pouvez diviser individuellement en différents dépôts comme bon vous semble:

  • Définition de l'API
  • Back-end
  • L'extrémité avant

La définition de l'API est une dépendance dans les deux autres projets, disons que vous utilisiez maven comme outil d'injection de dépendance. Ensuite, cela dépend de la rigueur avec laquelle vous souhaitez effectuer le contrôle de version. Vous pouvez augmenter la version du projet de définition d'API à chaque fois que vous apportez une modification pour vous assurer que les projets sont toujours dans un état compatible mais nécessitent plus de surcharge, ou vous pouvez utiliser quelque chose comme SNAPSHOTS dans maven, et ne faire le versionnement qu'une fois que vous sont satisfaits de l'interface qui est moins lourde mais vous pouvez souvent avoir des incompatibilités. Mais tant que vous appliquerez la définition de l'API dans votre front et votre back-end, vous diviserez bien les projets en différents référentiels.

Ces problèmes concernent davantage la gestion des dépendances. Même si les projets ne sont pas divisés et sont dans le même référentiel, il est assez facile de mettre le site Web dans un état où le front-end et le back-end ne sont pas synchronisés. Vraiment, la seule façon d'arrêter cela est de définir le contrat entre les deux, mais vous voulez le faire d'une manière qui ne couple pas les implémentations du front et du back-end, tout comme vous coderiez à la place à une interface d'une implémentation dans la programmation OO.

Aussi, pour gérer de manière préventive la critique selon laquelle cela crée une surcharge de maintenance de ce fichier d'interface, Swagger par exemple peut même produire des talons de code pour différents langages de programmation et frameworks tels que JAX-RS. Vous pouvez donc produire une interface dans la technologie de votre choix, puis implémenter cette interface. Cela ajoute également une très belle documentation à votre back-end, ce qui facilite la tâche des développeurs front-end.

Snickers3192
la source