IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Lors de la conception de systèmes logiciels, faites la chose la plus simple qui puisse fonctionner
Par Sean Goedecke

Le , par Sean Goedecke

0PARTAGES

3  0 
Lors de la conception de systèmes logiciels, faites la chose la plus simple qui puisse fonctionner, par Sean Goedecke.

Lorsque vous concevez des systèmes logiciels, faites la chose la plus simple qui puisse fonctionner.

Il est surprenant de voir jusqu'où vous pouvez aller en suivant ce conseil. Je pense sincèrement que vous pouvez l'appliquer à tout moment. Vous pouvez suivre cette approche pour corriger des bogues, pour maintenir des systèmes existants et pour en concevoir de nouveaux.

De nombreux ingénieurs conçoivent en essayant d'imaginer le système « idéal » : quelque chose de bien pensé, presque infiniment évolutif, élégamment distribué, etc. Je pense que c'est une approche totalement erronée de la conception de logiciels. Au lieu de cela, passez ce temps à comprendre en profondeur le système actuel, puis faites la chose la plus simple qui puisse fonctionner.

La simplicité peut être décevante

La conception de systèmes nécessite la maîtrise de nombreux outils différents : serveurs d'applications, proxys, bases de données, caches, files d'attente, etc. À mesure qu'ils se familiarisent avec ces outils, les ingénieurs juniors ont naturellement envie de les utiliser. C'est amusant de construire des systèmes à partir de nombreux composants différents ! Et c'est très satisfaisant de dessiner des cases et des flèches sur un tableau blanc, comme si vous faisiez de la véritable ingénierie.

Cependant, comme pour de nombreuses compétences, la véritable maîtrise implique souvent d'apprendre à en faire moins, et non plus. Le combat entre un novice ambitieux et un vieux maître est un cliché bien connu des films d'arts martiaux : le novice est un tourbillon de mouvements, de sauts et de pirouettes. Le maître reste quant à lui immobile. Mais d'une manière ou d'une autre, les attaques du novice ne semblent jamais atteindre leur cible, et l'attaque finale du maître est décisive.

En informatique, cela signifie qu'une bonne conception logicielle semble décevante. On dirait qu'il ne se passe pas grand-chose. Vous savez que vous êtes en présence d'une bonne conception logicielle parce que vous commencez à avoir des pensées telles que « oh, je ne m'étais pas rendu compte que le problème était si facile » ou « oh super, vous n'avez en fait rien de difficile à faire ».

Unicorn est une excellente conception logicielle, car elle offre toutes les garanties les plus importantes d'un serveur web (isolation des requêtes, mise à l'échelle horizontale, récupération après panne) en s'appuyant sur les primitives Unix. L'API REST Rails, norme industrielle, est une excellente conception logicielle, car elle vous offre exactement ce dont vous avez besoin pour une application CRUD de la manière la plus ennuyeuse possible. Je ne pense pas que ces logiciels soient impressionnants. Mais ce sont des prouesses impressionnantes en matière de conception, car ils font la chose la plus simple qui puisse fonctionner.

Vous devriez en faire autant ! Supposons que vous ayez une application Golang à laquelle vous souhaitez ajouter une sorte de limitation de débit. Quelle est la chose la plus simple qui puisse fonctionner ? Votre première idée pourrait être d'ajouter une sorte de stockage persistant (par exemple, Redis) pour suivre le nombre de requêtes par utilisateur à l'aide d'un algorithme de type « leaky bucket ». Cela fonctionnerait ! Mais avez-vous besoin d'une toute nouvelle infrastructure ? Et si, à la place, vous conserviez ces nombres de requêtes par utilisateur en mémoire ? Bien sûr, vous perdriez certaines données de limitation de débit lorsque l'application serait redémarrée, mais est-ce important ? En fait, êtes-vous sûr que votre proxy périphérique ne prend pas déjà en charge la limitation de débit ? Pourriez-vous simplement écrire quelques lignes dans un fichier de configuration au lieu d'implémenter la fonctionnalité ?

Peut-être que votre proxy périphérique ne prend pas en charge la limitation de débit. Peut-être que vous ne pouvez pas la suivre en mémoire parce que vous avez trop d'instances de serveur fonctionnant en parallèle, de sorte que la limite de débit la plus stricte que vous pourriez appliquer de cette manière est trop large. Peut-être que la perte des données de limitation de débit serait rédhibitoire, car les utilisateurs sollicitent votre service de manière excessive. Dans ce cas, la solution la plus simple qui pourrait fonctionner serait d'ajouter un stockage persistant, vous devriez donc le faire. Mais si vous pouviez adopter l'une des approches les plus simples, ne le feriez-vous pas ?

Vous pouvez vraiment créer une application entière à partir de zéro de cette manière : commencez par la chose la plus simple possible, puis ne l'étendez que lorsque de nouvelles exigences vous y obligent. Cela peut sembler ridicule, mais cela fonctionne. Considérez cela comme le principe de conception ultime YAGNI : au-dessus de la responsabilité unique, au-dessus du choix du meilleur outil pour le travail et au-dessus de la « bonne conception ».

Qu'y a-t-il de mal à faire la chose la plus simple ?

Bien sûr, il y a trois gros problèmes à toujours faire la chose la plus simple qui puisse fonctionner. Le premier est qu'en n'anticipant pas les exigences futures, vous vous retrouvez avec un système rigide ou un gros fouillis. Le deuxième est qu'il n'est pas clair ce que signifie « le plus simple », donc au pire, je dis « pour bien concevoir, faites toujours une bonne conception ». Le troisième est que vous devriez construire des systèmes évolutifs, et non des systèmes qui fonctionnent simplement pour le moment. Examinons ces objections dans l'ordre.

Gros enchevêtrements

Pour certains ingénieurs, « faire la chose la plus simple qui puisse fonctionner » revient à leur dire d'arrêter de faire de l'ingénierie. Si la chose la plus simple est généralement un bricolage rapide, cela signifie-t-il que ce conseil mènera inévitablement à un désordre complet ? Nous avons tous vu des bases de code avec des bricolages empilés les uns sur les autres, et elles ne ressemblent certainement pas à une bonne conception.

Mais les hacks sont-ils simples ? Je ne pense pas. Le problème avec un hack ou un kludge, c'est justement qu'ils ne sont pas simples : ils ajoutent de la complexité au code en introduisant un élément supplémentaire dont il faut toujours se souvenir. Les hacks sont simplement plus faciles à imaginer. Trouver la bonne solution est difficile, car cela nécessite de comprendre l'ensemble du code (ou une grande partie de celui-ci). En fait, la solution appropriée est presque toujours beaucoup plus simple que le hack.

Il n'est pas facile de faire la chose la plus simple qui puisse fonctionner. Lorsque vous examinez un problème, les premières solutions qui vous viennent à l'esprit ne sont généralement pas les plus simples. Pour trouver la solution la plus simple, il faut envisager de nombreuses approches différentes. En d'autres termes, cela nécessite un travail d'ingénierie.

Qu'est-ce que la simplicité ?

Les ingénieurs sont en désaccord sur ce qui constitue un code simple. Si « le plus simple » signifie déjà « avec une bonne conception », est-ce une tautologie de dire « vous devriez faire la chose la plus simple qui puisse fonctionner » ? En d'autres termes, Unicorn est-il vraiment plus simple que Puma ? L'ajout d'une limitation de débit en mémoire est-il vraiment plus simple que l'utilisation de Redis ? Voici une définition approximative et intuitive de la simplicité :

  1. Les systèmes simples ont moins de « pièces mobiles » : moins de choses à prendre en compte lorsque vous travaillez avec eux.
  2. Les systèmes simples sont moins connectés en interne. Ils sont composés de composants avec des interfaces claires et simples.

Les processus Unix sont plus simples que les threads (et donc Unicorn est plus simple que Puma) car les processus sont moins connectés : ils ne partagent pas la mémoire. Cela me semble tout à fait logique ! Mais je ne pense pas que cela vous donne les outils nécessaires pour déterminer ce qui est le plus simple dans chaque cas.

Qu'en est-il de la limitation de débit en mémoire par rapport à Redis ? D'un côté, la mémoire est plus simple car vous n'avez pas à vous soucier de tout ce qui est impliqué dans la mise en place d'un service séparé avec une mémoire persistante. D'un autre côté, Redis est plus simple car les garanties de limitation de débit qu'il offre sont plus directes : vous n'avez pas à vous soucier du cas où une instance de serveur considère qu'un utilisateur est limité en débit et une autre non.

Lorsque je ne sais pas ce qui me « semble » plus simple, j'aime utiliser ce critère décisif : les systèmes simples sont stables. Si vous comparez deux états d'un système logiciel et que l'un nécessite plus de travail continu si aucune exigence ne change, l'autre est plus simple. Redis doit être déployé et maintenu, il peut avoir ses propres incidents, il nécessite sa propre surveillance, il nécessite un déploiement distinct dans tout nouvel environnement dans lequel le service se trouve, etc. Ainsi, la limitation du débit en mémoire est plus simple que Redis.

Pourquoi ne voudriez-vous pas être évolutif ?

Certains ingénieurs s'écrient à présent : « Mais la limitation du débit en mémoire n'est pas évolutive ! » Faire la chose la plus simple qui puisse fonctionner ne permettra certainement pas d'obtenir le système le plus évolutif possible. Cela permettra d'obtenir un système qui fonctionne bien à l'échelle actuelle. Est-ce de l'ingénierie irresponsable ?

Non. À mon avis, le péché capital de l'ingénierie SaaS des grandes technologies est l'obsession de l'évolutivité. J'ai vu tant de souffrances inévitables causées par des systèmes surdimensionnés, conçus pour faire face à plusieurs ordres de grandeur de plus que l'échelle actuelle.

La principale raison de ne pas essayer cela est que cela ne fonctionne pas. D'après mon expérience, pour toute base de code non triviale, vous ne pouvez pas anticiper son comportement face à plusieurs ordres de grandeur de trafic supplémentaires, car vous ne savez pas à l'avance où se trouveront tous les goulots d'étranglement. Tout au plus, vous pouvez essayer de vous assurer que vous êtes prêt à faire face à un trafic deux ou cinq fois supérieur à l'actuel, puis vous tenir prêt à résoudre les problèmes au fur et à mesure qu'ils se présentent.

L'autre raison de ne pas essayer cela est que cela rend votre base de code rigide. Il est amusant de découpler votre service en deux parties afin qu'elles puissent être mises à l'échelle indépendamment (j'ai vu cela se produire peut-être dix fois, et je les ai vues être réellement mises à l'échelle indépendamment peut-être une fois). Mais cela rend certaines fonctionnalités très difficiles à mettre en œuvre, car elles nécessitent désormais une coordination sur le réseau. Dans le pire des cas, elles nécessitent des transactions sur le réseau, ce qui est un véritable problème d'ingénierie. La plupart du temps, vous n'avez tout simplement pas besoin de faire tout cela !

Conclusion

Plus je travaille dans le domaine des technologies, moins je suis optimiste quant à notre capacité collective à prédire l'évolution d'un système. Il est déjà assez difficile de comprendre où en est actuellement un système. Et en fait, c'est là la principale difficulté pratique pour réaliser une bonne conception : obtenir une compréhension précise et globale du système. La plupart des conceptions sont réalisées sans cette compréhension, et la plupart des conceptions sont donc assez mauvaises.

Il existe, en gros, deux façons de développer un logiciel. La première consiste à prédire à quoi pourraient ressembler vos besoins dans six mois ou un an, puis à concevoir le meilleur système pour répondre à ces besoins. La seconde consiste à concevoir le meilleur système pour répondre à vos besoins actuels : en d'autres termes, à faire la chose la plus simple qui puisse fonctionner.

Source : "Do the simplest thing that could possibly work"

Et vous ?

Pensez-vous que ces principes sont crédibles ou pertinents ?
Quel est votre avis sur le sujet ?

Voir aussi :

Les erreurs commises par les ingénieurs logiciels dans les grandes bases de codes établies, par Sean Goedecke

Développement de logiciels à long terme, par Bert Hubert

Le Manifeste anti-héritage : Écrire du code qui dure, par Mensur Durakovic
Vous avez lu gratuitement 751 articles depuis plus d'un an.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.

Une erreur dans cette actualité ? Signalez-nous-la !