Le gros projet sur lequel je travaille depuis quelques années est une application de contrôle (et tout) d'un appareil avancé, cœur de son firmware.
L'appareil est assez avancé, avec plus de fonctionnalités différentes que je pourrais dire de la mémoire, et 98% d'entre eux sont gérés par cet énorme exécutable. D'une part, le programme est assez maintenable, bien modulaire à l'intérieur, correctement documenté, il y a une séparation raisonnable des fonctionnalités par répertoires et fichiers et ainsi de suite.
Mais au final, tout est regroupé dans une seule application qui fait tout, de la communication de base de données à distance, la manipulation de l'écran tactile, la gestion d'une douzaine de protocoles de communication divers, les mesures, plusieurs algorithmes de contrôle, la capture vidéo, l'heure et la date du lever du soleil de Pâques (sérieusement, et ils sont nécessaire à des fins très sérieuses!) ... En général, les choses qui sont très peu reliées, souvent liées uniquement à certaines données qui ruissellent entre certains modules éloignés.
Cela peut se faire sous la forme de plusieurs exécutables distincts communiquant entre eux, par exemple via des sockets, dans un but plus spécifique, peut-être chargés / déchargés selon les besoins, etc. Aucune raison précise pour laquelle il est fait de cette façon.
D'une part, cela fonctionne, et cela fonctionne bien. Le projet est plus simple, sans maintenir la construction de plusieurs binaires. La structure interne est également plus facile, lorsque vous pouvez simplement appeler une méthode ou lire une variable au lieu de parler via des sockets ou de la mémoire partagée.
Mais d'un autre côté, la taille, l'échelle de cette chose me font peur, c'est comme piloter Titanic. On m'a toujours appris à modulariser, et tout regrouper dans un fichier gargantuesque me semble mal. Un problème que je connais est un plantage important d'un module (même insignifiant) qui plante tout - mais la qualité du code garantit que cela ne se produit pas vraiment dans les versions. Sinon, la séparation interne et la programmation défensive garantissent que cela fonctionnera toujours correctement, même si la moitié des modules internes échouent normalement pour une raison quelconque.
Quels autres dangers ai-je négligés? Pourquoi est-ce que ça me fait peur? Est-ce juste une peur irrationnelle de l'inconnu? Faire de gros projets sérieux de cette façon est-il une pratique acceptée? Soit calmer mes craintes ou me donner une bonne raison de refactoriser la version 2.0 en plusieurs binaires plus petits.
Réponses:
À l'exception du petit commentaire à la fin (deuxième, CPU superviseur), vous pourriez décrire mon entreprise. Oui, nous avons aussi besoin de Pâques.
Eh bien, nous sommes un peu plus loin. Nous avons divisé le grand exécutable et essayé d'utiliser des composants standard pour des bits standard. Pas exactement la grande amélioration que vous espérez. En fait, les performances deviennent une difficulté majeure, même sur un matériel plus costaud. Et les coûts de maintenance n'ont pas vraiment baissé maintenant qu'il y a des tonnes de code pour sérialiser et synchroniser les données sur des interfaces étroites.
La leçon que j'ai apprise? Avoir un seul exécutable est une solution éprouvée pour les petits systèmes, et nous avons des décennies d'expérience dans sa gestion. Tous nos outils le supportent nativement. La modularité peut également être effectuée dans un seul exécutable, et lorsque vous devez faire des compromis sur la modularité pour d'autres raisons, le piratage reste faible.
la source
Vous l'avez dit vous-même - il est assez maintenable, bien modulaire ...
À mon avis, et la plupart des membres de la direction seront d'accord avec moi sur ce point, des changements ne devraient jamais être apportés pour des raisons de changements. Il n'y a rien de mal à ce programme (votre description) à part qu'il ne suit pas les dernières tendances de programmation (qui sont mentionnées dans d'autres réponses).
Oui, c'est un programme plus vaste ... beaucoup l'ont été auparavant. Il est également bien documenté, donc tout ce que vous avez à faire est d'étudier son organisation interne et vous en verrez la logique. Je doute que tous ceux qui l'ont écrit avant vous étaient incompétents. Alors, explorez-le un peu, apprenez-le ... après cela, maintenez-le tel quel jusqu'à ce qu'une raison solide de le réécrire apparaisse. Votre manager vous sera reconnaissant d'avoir un bon rapport coût / effet.
Il vous fait peur parce qu'il est grand - mais encore une fois, personne ne s'attend à ce que vous appreniez tout par cœur en une semaine. Travaillez donc, morceau par morceau, petit à petit, jusqu'à ce que vous compreniez. En fin de compte, vous apprendrez qu'il est probablement bien organisé tel quel et comment il est organisé.
Si vous le réécriviez, vous accomplirez la même chose, mais en même temps, vous le ruinerez pour tous ceux qui étaient habitués à l'organisation précédente du code. De cette façon, vous seul devez vous "adapter".
la source
Je me sentirais mieux si vous disiez que vous aviez tout emballé dans des tests unitaires. Si vous ne le faites pas, vous devez créer une suite de tests avant même de penser à reconcevoir l'application.
Avoir une suite de tests améliorera votre compréhension de l'application et vous dira quand un changement dans l'application casse quelque chose.
la source
Si le code est bien modulaire avec un faible couplage et une forte cohésion, il est effectivement partitionné. Les frais généraux de communication devraient être plus faibles au sein d'un même processus que ce ne serait le cas avec plusieurs processus.
Pour les systèmes embarqués, votre seul processus peut éliminer la nécessité d'implémenter un O / S pour exécuter tous les processus que vous créez. Vous devrez également implémenter un système pour vérifier que tous les processus sont en cours d'exécution et les redémarrer si possible. Si ce n'était pas possible, vous devrez réagir en conséquence.
La division du code en exécutables séparés augmenterait également la taille globale du code source car vous auriez besoin d'implémenter tout le code client / serveur entre les modules. Vous auriez également besoin de plus de cas de test pour gérer ce code, et des cas où le processus serveur n'était pas là. Les grands projets sont grands, éliminer les complexités inutiles comme cela semble avoir été fait ici permet de les garder aussi petits que possible.
Les tests sont une sorte de hareng rouge ici, car les problèmes seront toujours là avec ou sans tests. De bons tests avec l'un ou l'autre choix de conception devraient certainement être encouragés. Je m'attends à ce que les tests soient plus simples avec le code monolithique.
Forcer un crash lorsque cela est nécessaire est également plus facile avec un exécutable monolithique.
la source
Si je devais travailler sur une si grande application monolithique, les choses qui me feraient peur sont le manque d'encapsulation, la faible cohésion, le couplage élevé et la façon de tester tout cela. Les gros monolithes sont souvent une invitation à abandonner une architecture propre et à entamer la descente dans une grosse boule de boue .
Une fois que cela se produit, les tests deviennent un cauchemar et vous êtes bien sur la voie de l'obsolescence.
Cependant, si votre code est bien structuré et bien maintenu et que les principes de haute cohésion, de faible couplage et d'encapsulation appropriée sont respectés, je ne vois pas de raison de le refactoriser en unités plus petites.
Vous voulez cependant avoir de bons tests en place.
la source
Idéalement, la réponse est oui. Avoir plusieurs binaires a le potentiel de le rendre plus stable ....
... cependant, vous avez également mentionné que le code dans la base de code monolithique est bien écrit et modularisé, ce qui signifie que vous n'avez pas à faire face à un énorme gâchis enchevêtré, juste une énorme base de code ...
Dans l'ensemble, je dirais que le dicton applicable est: "Ne laissez pas le parfait être l'ennemi du bien." Bien que je pense que vous avez raison de dire qu'une base de code monolithique est mauvaise, je pense également que cela ne vaut pas la peine de s'en inquiéter.
Il serait raisonnable d'avancer en mettant de nouveaux morceaux de code dans leurs propres binaires, mais revenir en arrière et refactoriser ne vaut probablement pas la peine.
la source
Si vous utilisez un seul processus, il est possible qu'un pointeur sauvage provenant de l'un de vos sous-modules endommage un morceau de mémoire critique (et bloque le tout).
Si vous utilisez différents processus, vous n'utilisez pas au moins le même tas ou la même pile d'appels et il peut également être plus facile de redémarrer un seul sous-processus.
La division de tout en plusieurs processus vous obligera également à nettoyer la conception et à éviter que chaque module ne commence à utiliser les méthodes internes des autres modules.
Cela étant dit, c'est probablement une tâche ardue.
Ce que vous pourriez commencer à faire, c'est de décider que chaque nouveau module devrait être un processus isolé.
la source
Lorsque j'arrive dans le domaine où le projet est trop grand pour tout faire d'un coup, je construis un tableau à accrocher au mur qui présente toutes les grandes sections et comment elles s'emboîtent. Ensuite, lorsque je travaille, je peux référencer le module sur lequel je me concentre et avoir un rappel visuel de la façon dont il s'intègre dans l'ensemble.
Il est tout à fait possible que la complexité et les dangers de la maintenance d'un système de génération et de versionnage pour plusieurs binaires déconnectés l'emportent de loin sur votre malaise avec un projet aussi vaste et monolithique.
la source