Pour les éditeurs de logiciels, ces meilleures pratiques pourraient vous faire gagner du temps et éviter à vos développeurs d’avoir des maux de tête. En général, les programmeurs sont un groupe d’opinion, et les opinions fortes sont souvent le signe d’une grande passion. Dans cette optique, n’hésitez pas à exprimer votre désaccord sur ces points. De plus, il s’agit d’une liste non définitive et non exhaustive de principes qui doit être appliqués avec sagesse et souplesse. Nombreux de ces principes sont liés aux pratiques et aux idéaux de tests. Certains de ces principes sont spécifiques à Python, mais la plupart ne le sont pas.

Développer et tester les meilleures pratiques des éditeurs de logiciels:

1 – YAGNI: “You Ain’t Gonna Need It

C’est un principe de l’Extreme Programming (et d’une certaine manière liée à la Lean Thinking) qui stipule qu’un programmeur ne doit pas ajouter de fonctionnalité avant que cela ne soit complètement nécessaire. Même si vous êtes totalement sûr d’avoir besoin d’une fonctionnalité ou d’un morceau de code plus tard, ne l’implémentez pas maintenant. Il est fort probable que vous n’en aurez pas besoin après tout ou que ce dont vous avez réellement besoin est très différent de ce dont vous aviez prévu d’avoir besoin auparavant.

2 – Code Répétitif

La troisième fois que vous écrivez le même morceau de code, c’est le bon moment pour l’extraire dans une aide polyvalente (et écrire des tests pour celle-ci). Les fonctions d’aide dans un test n’ont pas besoin d’être testées ; lorsque vous les sortez et les réutilisez, elles ont besoin d’être testées. La troisième fois que vous écrivez un code similaire, vous avez tendance à avoir une idée claire de la forme du problème général que vous êtes en train de résoudre.

3 – Conception de l’API pour les éditeurs de logiciels

En ce qui concerne la conception de l’API (face externe et objet API) : les choses simples doivent être simples ; les choses complexes doivent être possibles. Il faut d’abord concevoir pour le cas simple, avec de préférence une configuration ou un paramétrage nul, si cela est possible. Ajouter des options ou des méthodes API supplémentaires pour les cas d’utilisation plus complexes et plus souples (selon les besoins).

4 – Fail fast ou Échouer rapidement.

Vérifiez la saisie et échouez sur une saisie absurde ou un état non valide le plus tôt possible, de préférence avec une exception ou une réponse d’erreur qui fera apparaître clairement le problème exact à votre interlocuteur. Autorisez toutefois les cas d’utilisation “novateurs” de votre code (c’est-à-dire ne faites pas de vérification de la saisie pour la validation de l’entrée, sauf si vous en avez vraiment besoin).

5 – Les tests unitaires

Les tests unitaires portent sur l’unité de comportement, et non sur l’unité de mise en œuvre. L’objectif est de modifier l’implémentation, sans changer le comportement ou devoir modifier l’un de vos tests, bien que ce ne soit pas toujours possible. Donc, lorsque c’est possible, traitez vos objets de test comme des boîtes noires, en les testant via l’API publique sans faire appel à des méthodes privées ou en bricolant avec l’état.

Pour certains scénarios complexes – comme le test de comportement sur un état complexe spécifique pour trouver un bug obscur – cela peut ne pas être possible. Écrire des tests en premier vous aide vraiment dans ce domaine car cela vous oblige à réfléchir au comportement de votre code et à la manière dont vous allez le tester avant de l’écrire. Le fait de tester en premier encourage les unités de code plus petites et plus modulaires, ce qui signifie généralement un meilleur code. Une bonne référence pour commencer l’approche “test first” est “Test Driven Development by Example“, de Kent Beck.

Pour les tests unitaires (y compris les tests d’infrastructure), tous les chemins de code doivent être testés. Une couverture à 100 % est un bon point de départ. Vous ne pouvez pas couvrir toutes les permutations/combinaisons d’états possibles (explosion combinatoire), il faut donc en tenir compte. Ce n’est que s’il y a une très bonne raison que les chemins de code ne doivent pas être testés. Le manque de temps n’est pas une bonne raison et finit par coûter plus de temps. Parmi les bonnes raisons possibles, on peut citer : l’impossibilité réelle de tester (de manière significative), l’impossibilité de toucher en pratique ou la couverture d’un autre test. Un code sans tests est un handicap. Mesurer la couverture et rejeter les RP qui réduisent le pourcentage de couverture est une façon de s’assurer que vous progressez progressivement dans la bonne direction.

Les tests unitaires plus petits et plus étendus donnent des informations plus précieuses en cas d’échec – ils vous indiquent précisément ce qui ne va pas. Un test qui résiste à la moitié du système pour tester le comportement demande plus d’investigation pour déterminer ce qui ne va pas. En général, un test qui prend plus de 0,1 seconde à s’exécuter n’est pas un test unitaire. Il n’existe pas de test unitaire lent. Avec un comportement de test des tests unitaires à portée étroite, vos tests agissent comme une spécification de facto pour votre code. Idéalement, si quelqu’un veut comprendre votre code, il devrait pouvoir se tourner vers la suite de tests comme “documentation” du comportement.

6 – Les Codes et les commentaires

Inévitablement, les commentaires de code deviennent des mensonges avec le temps. En pratique, peu de personnes mettent à jour les commentaires lorsque les choses changent. Efforcez-vous de rendre votre code lisible et auto-documenté grâce à de bonnes pratiques de dénomination et à un style de programmation connu. Le code qui ne peut pas être rendu évident – en contournant un bug obscur ou une condition improbable, ou une optimisation nécessaire – doit être commenté. Commentez l’intention du code, et pourquoi il fait quelque chose plutôt que ce qu’il fait.

7 – Écrivez sur la défensive

Pensez toujours à ce qui peut mal tourner, à ce qui se passera sur une entrée non valide et à ce qui pourrait échouer, ce qui vous aidera à détecter de nombreux bogues avant qu’ils ne surviennent.

8 – L’injection de dépendance

L’injection de dépendance est un modèle de codage utile pour savoir clairement quelles sont vos dépendances et d’où elles viennent. (Faites en sorte que les objets, les méthodes, etc. reçoivent leurs dépendances comme paramètres plutôt que d’instancier eux-mêmes de nouveaux objets). Cela rend les signatures API plus complexes, il s’agit donc d’un compromis. Se retrouver avec une méthode qui a besoin de 10 paramètres pour toutes ses dépendances, est un bon signe que votre code en fait trop.

9 – Gérer les API externes

Les API orientées vers l’extérieur sont celles où la “conception en amont” – et la prise en compte des cas d’utilisation futures – sont vraiment importantes. Changer d’API est une corvée pour nous et pour nos utilisateurs, et créer une incompatibilité à rebours est terrible (bien que parfois impossible à éviter). Concevez soigneusement les API externes, tout en respectant le principe “les choses simples doivent être simples”.

10 – Respectez le nombre de ligne de code

Pour les éditeurs de logiciels, si une fonction ou une méthode dépasse 30 lignes de code, envisagez de la décomposer. Une bonne taille maximale de module est d’environ 500 lignes. Les fichiers de tests ont tendance à être plus longs que cela.

11 – DRY (Don’t Repeat Yourself)

DRY (Don’t Repeat Yourself) importe beaucoup moins dans les tests que dans le code de production. La lisibilité d’un fichier de test individuel est plus importante que la maintenabilité (séparation des morceaux réutilisables). C’est parce que les tests sont exécutés et lus individuellement plutôt que de faire eux-mêmes partie d’un système plus large. De toute évidence, une répétition excessive signifie que des composants réutilisables peuvent être créés pour plus de commodité, mais c’est beaucoup moins préoccupant que pour la production.

12 – Refactorisation

Refactorisez chaque fois que vous voyez le besoin et que vous en avez l’occasion. La programmation concerne les abstractions et plus vos abstractions sont proches du domaine du problème, plus votre code est facile à comprendre et à maintenir. Au fur et à mesure que les systèmes se développent de manière organique, ils doivent changer de structure pour leur cas d’utilisation en expansion. Les systèmes dépassent leurs abstractions et leur structure et ne pas les changer devient une dette technique qui est plus pénible (et plus lente et plus boguée) à contourner. Inclure le coût de la compensation de la dette technique dans les estimations pour les travaux sur les fonctionnalités. Plus vous laissez la dette, plus les intérêts qu’elle accumule sont élevés. Un excellent livre sur la refactorisation et les tests: Working Effectively with Legacy Code , par Michael Feathers.

13 – Code correct et rapide

Faites en sorte que le code soit correct en premier et rapide en second. Lorsque les éditeurs de logiciels travaillent sur des problèmes de performances, établissez toujours un profil avant d’apporter des correctifs. Habituellement, le goulot d’étranglement n’est pas tout à fait là où vous le pensiez. Écrire du code obscur parce qu’il est plus rapide ne vaut la peine que si vous avez profilé et prouvé que cela en vaut vraiment la peine. L’écriture d’un test qui exerce le code que vous profilez avec un timing autour de celui-ci permet de savoir plus facilement quand vous avez terminé et peut-être laissé dans la suite de tests pour éviter les régressions de performances. 

14 – Not Invented Here

“Not Invented Here” n’est pas aussi mauvais que les gens le disent. Si nous écrivons le code, nous savons ce qu’il fait, nous savons comment le maintenir et nous sommes libres de l’étendre et de le modifier comme bon nous semble. Cela suit le principe YAGNI: nous avons un code spécifique pour les cas d’utilisation dont nous avons besoin plutôt qu’un code à usage général qui est complexe pour des choses dont nous n’avons pas besoin. D’un autre côté, le code est l’ennemi, et posséder plus de code que nécessaire est mauvais. Tenez compte du compromis lors de l’introduction d’une nouvelle dépendance.

15 – Propriété Partagée

La propriété partagée du code est l’objectif; la connaissance cloisonnée est mauvaise. Au minimum, cela signifie discuter ou documenter les décisions de conception et les décisions de mise en œuvre importantes. La révision du code est le pire moment pour commencer à discuter des décisions de conception, car l’inertie nécessaire pour effectuer des changements radicaux après l’écriture du code est difficile à surmonter. 

16 – Soyons ingénieurs!

Comme tous éditeurs de logiciels, pensons à la conception et à la construction de systèmes robustes et bien implémentés, plutôt qu’à la croissance de monstres organiques. Cependant, la programmation est un exercice d’équilibre. Nous ne construisons pas toujours une fusée. La sur-ingénierie (architecture d’oignon) est aussi pénible à travailler qu’un code sous-conçu. Presque tout ce qui est écrit par Robert Martin vaut la peine d’être lu,  Clean Architecture: A Craftsman’s Guide to Software Structure and Design est une bonne ressource sur ce sujet. 

17 – Insérez un bug délibéré

Toujours voir votre test échouer au moins une fois. Insérez un bug délibéré et assurez-vous qu’il échoue, ou exécutez le test avant que le comportement testé ne soit terminé. Sinon, vous ne savez pas que vous testez vraiment quoi que ce soit. Écrire accidentellement des tests qui ne testent rien ou qui ne peuvent jamais échouer est facile.

18 – Dernier point pour les éditeurs de logiciels

Et enfin, un dernier point pour les éditeurs de logiciels : le broyage constant des fonctionnalités est une mauvaise méthode de développer des logiciels. Ne pas s’occuper de la dette technique ralentit le développement et aboutit à un produit dérisoire en plus de bogué. Ne pas laisser les développeurs être fiers de leur travail vous garantit de ne pas en tirer le meilleur parti.

Envie d’une équipe professionnelle qui respecte les normes et bonnes pratiques pour vos projets informatiques !

Demandez un Devis Chez Hairun Technology