From bc1d70343807104ccf64b6bde9b2db54270203ff Mon Sep 17 00:00:00 2001 From: neodarz Date: Fri, 10 Mar 2017 11:58:22 +0100 Subject: Initiale release --- ...ps_de_dveloppement_sans_sacrifier_la_qualit.txt | 491 +++++++++++++++++++++ 1 file changed, 491 insertions(+) create mode 100644 Rduire_les_temps_de_dveloppement_sans_sacrifier_la_qualit.txt (limited to 'Rduire_les_temps_de_dveloppement_sans_sacrifier_la_qualit.txt') diff --git a/Rduire_les_temps_de_dveloppement_sans_sacrifier_la_qualit.txt b/Rduire_les_temps_de_dveloppement_sans_sacrifier_la_qualit.txt new file mode 100644 index 0000000..7936251 --- /dev/null +++ b/Rduire_les_temps_de_dveloppement_sans_sacrifier_la_qualit.txt @@ -0,0 +1,491 @@ +Titre: Réduire les temps de développement sans sacrifier la qualité +Auteur: LeBouquetin +Date: Tue 21 Feb 2017 15:26:31 +0100 +Lien: https://linuxfr.org/users/lebouquetin/journaux/reduire-les-temps-de-developpement-sans-sacrifier-la-qualite + +Sommaire + + * Avant-propos[1] + * Introduction[2] + * Comment réduire les temps de développement ?[3] + * Réduction de temps de développement avec perte de qualité[4] + * Réduction de temps de développement sans perte de qualité[5] + + * Quel intérêt ?[6] + * Quelques techniques[7] + + * Technique n°1 — ne pas prendre de décision à la place du client[8] + * Technique n°2 — se limiter strictement au cahier des charges[9] + * Technique n°3 — réduire le périmètre fonctionnel[10] + * Technique n°4 — faire du « test-driven » sur les interfaces[11] + * Technique n°5 — Faire de la conception progressive sur les composants + internes[12] + * Technique n°6 — Diviser pour mieux régner[13] + * Technique n°7 — Écrire du code simple[14] + * Technique n°8 — Écrire un script de setup d'un environnement fonctionnel[15] + + + * Conclusion[16] + +Avant-propos + +Ce journal parle de développement logiciel, de coûts, de qualité. Il est +question de méthode et méthodologie plutôt que de technologies. + +Ce journal est un peu long… vous êtes averti(e)s ;) + +Introduction + +Je dirige une entreprise dont l'activité principale (en terme de chiffre +d'affaire) est de faire de la prestation de services : développements +techniques, développements d'applications distribuées et applications web sur +mesure. + +Régulièrement les prospects et clients trouvent que les coûts sont élevés et +cherchent à les réduire. C'est naturel. + +Le métier de la prestation de service est de vendre des jours/homme. Lorsqu'on +veut réduire les coûts, le premier réflexe est donc de réduire le nombre de +jours/homme. + +La problématique que j'essaie d'adresser ici est donc d'être capable de +répondre à ces attentes, et si possible sans que ce soit au détriment de la +qualité. + +client : dans cet article, le client est soit un client de l'entreprise soit un +client interne — responsable produit, équipe marketing, utilisateur de l'outil +interne, etc. + +anglicismes : par avance, merci de ne pas me tenir rigueur des anglicismes +présents dans ce journal. Le quotidien du développeur est fait d'anglicismes — +pour le meilleur et pour le pire. + +Comment réduire les temps de développement ? + +La manière la plus simple de réduire les temps de développement est d'adopter +une stratégie de développement "agressive". Cette stratégie "agressive" +signifie avec perte de qualité. + +Analogie : on pourrait faire un parallèle avec la compression d'images. Selon +les algorithmes, la compression sera avec ou sans perte d'information. De la +même manière, on pourra réduire les temps de développement avec ou sans perte +de qualité — mais avec des niveaux de "compression" différents. + +Réduction de temps de développement avec perte de qualité + +A algoo, nous proposons 3 stratégies de développement qui essaient d'adresser +différentes attentes en terme de temps de développement (mais également de +qualité) : + + 1.le développement de "prototype". On développe un démonstrateur, qui sera + jetable. Ca permet de valider un concept, faire des démos rapidement, mais + sans pérennité du code. Le coût en temps est réduit au maximum + puisqu'aucune réflexion sur la pérennité n'est menée. + 2.le développement de mvp[17]" — produit minimum viable. C'est un concept très + prisé par les startups ; pour simplifier il s'agit d'implémenter le minimum + de fonctionnalités dans un produit pour le rendre commercialisable. Dans ce + type de développement, les principales problématiques de pérennité seront + prises en compte, mais pas les autres. On aura alors un logiciel + "moyennement évolutif". + 3.le développement en mode "ingénierie logicielle". C'est la stratégie de + développement long-terme : on fait de l'architecture, de la conception, des + tests et de l'intégration continue — tests unitaires, test d'intégration, + tests fonctionnels, etc. C'est la stratégie que l'on favorise lorsqu'on + intervient sur un logiciel dont la durée de vie ciblée est de plusieurs + années. + +Les stratégies 1 et 2 sont moins couteuses que la 3, mais la qualité est +partiellement sacrifiée et le niveau de finition également en faveur d'un temps +de développement réduit (à court terme) + +Réduction de temps de développement sans perte de qualité + +Je vais vous présenter ici les techniques que nous mettons en oeuvre pour +réduire les coûts de développement sans pour autant réduire la qualité (voire +au contraire). + +Quel intérêt ? + +Réduire les coûts sans sacrifier la qualité est évidemment intéressant pour +l'entreprise ou le client car : +- soit vous avez la même chose pour un coût inférieur, +- soit vous avez un périmètre fonctionnel plus étendu pour le même prix. + +Du côté des développeurs, en revanche, on a parfois l'impression qu'on essaie +de brader les compétences. + +En réalité il ne s'agit pas de brader des compétences mais : + + * d'industrialiser ce qui peut l'être sans surcoût, + * d'optimiser l'exploitation des compétences de chacun, des développeurs, + certes, mais aussi des autres intervenants, et en particulier des clients. + +Quelques techniques + +Les différentes techniques présentées ci-dessous ne sont pas exhaustives et ne +demandent qu'à être complétées via vos commentaires. Elles ne sont pas non plus +systématiques car chacune s'appliquera plus ou moins bien selon le projet, +l'état d'avancement, les interlocuteurs — client interne, externe, client +compétent techniquement, béotien, etc. + +Technique n°1 — ne pas prendre de décision à la place du client + +Objectif : éviter à tout prix de redévelopper une fonctionnalité non conforme +aux attentes + +Quand on développe un logiciel, on le fait toujours pour un client. Ce client +peut être externe — c'est le cas classique d'un prestataire de service, interne +- on développe un logiciel à partir des demandes du marketing ou du responsable +produit, ou encore "perso" (on développe un outil dont on a soi-même besoin). + +Les cahiers des charges et spécifications ne sont jamais complets. Il y a alors +systématiquement des sujets à trancher. + +Les développeurs ont besoin de réponses pour avancer, mais ils n'ont pas la +connaissance pour décider. Je parle bien de connaissance, pas de compétence : +seul le client sait ce qu'il veut, et si le développeur sait ce dont le client +a réellement besoin (cas typique : un client qui ne connait rien au métier du +logiciel), il n'est toutefois pas en mesure de trancher seul. + +Pourquoi ? Parce que la solution "idéale" qu'il aura implémentée ne sera pas +celle que le client veut donc probablement que le client ne sera pas satisfait +(ce qui n'est pas bon), ou alors dans le pire des cas il va vouloir que le +boulot soit refait, ce qui implique "re-développement", et dans tous les cas +négociations, qui peuvent être coûteuses en temps (donc couteuses tout court, +et pas la partie la plus agréable). + +Exemple : un cahier des charges qui indiquerait "l'utilisateur doit pouvoir +être notifié". + + * Est-ce qu'on veut des notifications par email ? des notifications sur sa + webapp ? par SMS ? Toutes ? + * Est-ce que l'utilisateur choisi lui-même quand et comment il est notifié ? + +Pour caricaturer, dans ce cas de figure, un développeur va avoir 2 réflexes : +- soit il va aller droit au but - avec un système de notifications tel qu'il +l'imagine, lui, +- soit il va concevoir et implémenter un système complètement flexible. + +Dans le premier cas, la solution ne sera pas évolutive, et s'il est parti sur +de mauvaises bases, c'est potentiellement tout son travail qui sera à refaire, +sans compter la frustration d'avoir "travaillé pour rien" ou "de ne pas être +écouté". + +Dans le second cas, on aura une solution "over-engineered" — de la surqualité, +qui a un coût. + +La solution : au moindre choix, il faut demander au client. Et idéalement au +client final. On a vite fait de "savoir" lorsqu'on est chef de projet ou +responsable produit, mais la réalité est la même : c'est le client qui sait. + +Technique n°2 — se limiter strictement au cahier des charges + +Objectif : développer uniquement ce qui est commandé. + +Lorsque le client a rédigé son cahier des charges, il a pris le temps de +réfléchir à ce dont il avait besoin. Idéalement, il fournira en plus du cahier +des charge, un cahier des charges prévisionnel pour les versions suivantes, ou +une stratégie d'évolution envisagée. Cette stratégie va permettre d'opérer des +choix techniques et architecturaux mais ne doit en aucun cas générer de coûts +de développement supplémentaires. + +Parfois, en tant que développeur, on se dit "ah mais si je fais ça de telle +manière, ça prend juste quelques jours de plus et ça permet d'avoir un système +plus évolutif". Et en général plus complexe, donc plus fragile. + + * D'une part si cette flexibilité n'est exprimée nulle part il ne faut pas la + prendre en compte — il sera temps de l'implémenter le jour où elle sera + vraiment demandée (si ce jour arrive), + * d'autre part en règle général, plus un système est flexible, plus il est + complexe, plus il est fragile. Donc plus il nécessite de tests pour être + certifié "conforme". + +Si une certaine flexibilité est obtenue avec quelques jours de plus, il sera +probablement préférable soit de ne rien faire, soit de prendre ces quelques +jours pour blinder la couverture de tests automatiques. Ainsi, le code reste +simple et robuste, et la future évolution sera plus facile à implémenter car +toute régression sera décelée via les tests automatiques. + +Technique n°3 — réduire le périmètre fonctionnel + +Objectif : développer uniquement ce qui est nécessaire. + +Lorsqu'un client écrit un cahier des charges, il ne prend pas toujours le temps +de prendre le recul nécessaire pour évaluer la pertinence de ses attentes. +Concrètement, il va indiquer dans son cahier des charges l'ensemble des +fonctionnalités qu'il attend, mais lorsqu'on présente un chiffrage mettant en +relief le temps de développement associé à chaque fonctionnalité, il n'est pas +rare que l'urgence de certains points devienne toute relative. + +Technique n°4 — faire du « test-driven » sur les interfaces + +Objectif : (dé)terminer les spécifications de manière exhaustive. + +Nous développons des applications web et des applications distribuées. Cela +signifie en particulier la définition d'APIs (REST pour la majorité, mais pas +nécessairement). + +*Note : API signifie Application Programming Interface, il s'agit des +interfaces qui vont permettre de piloter votre logiciel. +* + +Ces interfaces sont potentiellement très différentes du code qui est dessous, +mais une chose est sûre : lorsqu'on entame le développement, on ne sait pas +exactement ce qu'on doit faire. + +Dans le meilleur des cas, une partie du travail consiste à "finir les +spécifications" +(par exemple la gestion des cas d'erreur), dans le pire des cas, la tâche de +développement consiste aussi à écrire les spécifications. + +La meilleure manière de faire ce travail de spécification — en tout cas de le +terminer, est d'écrire les cas de test. Cela permet de se concentrer sur +l'utilisation (donc le besoin) et non sur l'implémentation (la manière de faire +les choses). + +Concrètement l'idée est d'écrire tous les cas de test puis de les exécuter à +chaque nouvelle itération du développement. D'un taux initial de 100% d'erreur, +on va finir par arrriver à 100% OK une fois que le développement est terminé. + +Ca n'aura pas coûté plus cher et le gain sera double : + + * une conception adaptée au besoin, + * un refactoring simplifié car spécification complète (via les cas de test) + et 100% testée + +Dans l'hypothèse où l'on aurait commencé par le développement, puis écrit les +tests, outre le fait qu'on prend le taureau par les cornes (on écrit la +solution avant d'avoir écrit les spécifications), le risque est d'avoir conçu +une solution qui ne va pas répondre à 100% des besoins, ce qui veut dire qu'on +doit faire du refactoring ou qu'on accepte une couverture partielle des +besoins. + +Faire du "test driven" sur les interfaces est une bonne chose, mais sur le +reste ? Pas forcément. + +Les meilleures spécifications qu'on pourra obtenir/définir seront toujours sur +les interfaces. Dans ce contexte écrire les tests revient simplement à +transcrire les spécifications en code. + +Lorsqu'on travaille sur le développement (et la conception) interne(s), on a +rarement des spécifications, on va plus (+) avancer à tâtons, ce qui correspond +à la technique suivante. + +Technique n°5 — Faire de la conception progressive sur les composants internes + +Objectif : avoir quelque chose de fonctionnel le plus rapidement possible. + +Lorsqu'on conçoit un nouveau module logiciel, on sait rarement à l'avance +exactement comment il doit être conçu et comment il va se comporter. + +En général, on découvre la réelle complexité progressivement parce qu'on pense +au fur et à mesure à l'ensemble des cas de figure. Ce qui semblait initialement +simple devient (très) compliqué ; du coup la question de « refactoriser » le +code devient récurrent, etc, etc. + +L'idée ici est donc d'avoir au plus vite quelque chose qui fonctionne, et +seulement ensuite de se poser la question de comment concevoir le module pour +découper la complexité. + +On assimile le métier via une première version "brouillon" puis on modélise ce +métier proprement. + +Cela signifie en gros : + + 1.développer le composant fonctionnant dans le cas nominal en mode "proto" + 2.au cours du 1. on identifie naturellement les cas d'erreur possibles / à + traiter (et on les documente d'une manière ou d'une autre dans le code) + 3.on a un code fonctionnel, il est alors temps : + + 1.de rationaliser la conception, + 2.de gérer/traiter les cas d'erreur, + 3.d'écrire les tests unitaires une fois les composants clairement découpés. + + +On sépare les tâches d'assimilation métier et de modélisation logicielle. + +Et pour la modélisation logicielle, rien de tel que la technique suivante. + +Technique n°6 — Diviser pour mieux régner + +Objectif : simplifier la maintenance et l'évolutivité du code. + +Les développeurs n'aiment que rarement faire de la maintenance sur du code. +Personnellement, je trouve que c'est la partie la plus passionnante du travail +car les contraintes sont fortes. + +Bien souvent les jeunes diplômés (en particulier) ne veulent pas faire de +maintenance mais veulent concevoir un logiciel entier. Et lorsqu'il s'agit de +reprendre du code, plutôt que le faire évoluer ils préfèrent souvent tout +refaire de zéro. + +Pourtant le code a une intelligence propre : il répond à des besoins métiers +qui ont été accumulés en son sein, et lorsqu'on ré-écrit un logiciel +complètement, c'est une grande partie de cette connaissance que l'on perd. + +La raison pour laquelle la maintenance n'est pas sexy est que très souvent le +code est un véritable nœud de fonctionnalités et responsabilités, et lorsqu'on +aperçoit un bout et que l'on essaie de tirer sur la ficelle, c'est toute la +pelote qui vient. Et plus on tire, plus les nœuds se resserrent ;) + +Hors, ré-écrire un code complet est très coûteux, et carrément exorbitant si on +considère qu'on veut réécrire à périmètre fonctionnel constant. (attention : +toutes les ré-écritures n'ont pas pour objectif d'être iso-fonctionelles) + +La bonne solution pour faciliter la maintenance est de découper le code pour +qu'il soit plus facile de le faire évoluer. + +le principe est simple : "un module = une responsabilité". Et inversement. + +Prenons un exemple concret : je veux faire un logiciel de gestion de tâches +(todo). J'identifie spontanément 3 responsabilités : + + 1.le stockage des informations + 2.l'intelligence du logiciel - le noyau + 3.l'interface "utilisateur" (ça peut être une API REST) + +Du MVC ? Si on veut, mais c'est le niveau 1. du découpage. + +Imaginons, qu'on ait développé le dit logiciel en 3 modules : une interface +utilisateur en QT, un module "kernel" qui implémente l'intelligence et un +module "database" qui stocke dans une base SQLite. + +Un jour, mon client me demande s'il est possible de faire une interface web et +qu'il aimerait pouvoir stocker dans une base de données MySQL, voire MongoDB. + +Il y a du travail, mais on va pouvoir découper ça relativement facilement. Par +rapport au cas précédent, les 3 modules se décomposent désormais 7 modules (7 +responsabilités) : + + 1.l'instanciation du "driver de données" (une fabrique) + 2.le driver SQLite (qui va reprendre grosso modo le code de l'ancien composant + "stockage des informations") + 3.le driver MongoDB (qui va implémenter une interface compatible avec l'ancien + composant "stockage des informations") + 4.le noyau du logiciel (qui va désormais manipuler des objets génériques) + 5.l'interface utilisateur QT + 6.Un composant "web" qui implémente une API REST/Json + 7.Un composant "web" frontend qui se connecte sur l'API. + +Les composants 1, 2 et 3 de l'ancien logiciel sont quasiment naturellement les +2, 4 et 5 du nouveau. + +Dans cette décomposition, chaque composant a une unique responsabilité. Et +chaque responsabilité est gérée par un composant unique. + +Note : les parties en gras qui signifient "et inversement" mettent en relief +qu'une responsabilité qui serait découpée entre plusieurs composants rend la +maintenance et l'évolutivité aussi difficile que des composants aux +responsabilités multiples. + +L'illustration caricaturale dans mon domaine est Django, qui n'incite en aucun +cas à faire ce type de découpage : + + * Les vues Django implémentent de l'intelligence "métier" (et donc si on veut + ré-implémenter les même fonctionnalités via une autre interface, c'est un + gros travail de refactoring), + * La validation des données va être implémentée dans des formulaires ou dans + les modèles eux-même voire dans des "ModelView", + * Lorsqu'on travaille avec Django Rest Framework (la seule vraie raison + d'utiliser Django en 2017), les serializers risquent d'implémenter toute la + logique — et de fait devenir le "kernel" partiel d'une application. + +Bien entendu on peut exploiter Django différemment, mais la pratique que l'on +constate est celle-ci. + +Technique n°7 — Écrire du code simple + +Objectif : simplifier la maintenance et l'évolutivité du code. + +« Ce qui se conçoit bien s'énonce clairement » — Nicolas Boileay-Despréaux. + +Souvent les développeurs veulent faire des choses compliquées, et exploiter les +fonctionnalités d'un langage au maximum. Mais la réalité c'est qu'un logiciel +qui est écrit simplement fonctionne aussi bien qu'un logiciel exploitant toutes +les particularités d'un langage. + +Il fonctionne aussi bien, et ça ne coûte pas plus cher à écrire. +Et il coûte beaucoup moins cher à lire, donc à comprendre, donc à maintenir et +faire évoluer. + +Parfois les arguments sont d'écrire du code concis ou du code "esthétique", +mais la concision trop extrême devient inaccessible. + +Analogie : prenons un four qui propose 2 boutons rotatifs - un pour régler la +position et un pour régler la température. Ce four est beaucoup plus simple à +appréhender qu'un four avec un unique bouton rotatif sur lequel on peut +"cliquer" pour entrer dans des menus. Esthétiquement certains diront que c'est +plus joli - c'est subjectif. Par contre ce qui n'est pas subjectif, c'est que +c'est moins naturel à utiliser et moins ergonomique. Pour le confirmer, il +suffit de regarder ce qui se fait dans les cuisines des restaurants : on +industrialise la cuisine, et les choix qui sont opérés sont représentatif : du +gaz, des boutons ronds, un bouton par fonctionnalité. + +Quand vous codez, imaginez que vous êtes un chef étoilé :) + +Technique n°8 — Écrire un script de setup d'un environnement fonctionnel + +Objectif : monter un environnement de test en 1 minute + +Dans l'esprit du Joel Test[18], il est de bon ton d'écrire un script de setup +qui permet d'obtenir un environnement fonctionnel en une commande voire deux. +Cela permet de très rapidement intégrer un nouveau développeur (ou un nouveau +poste de développement), cela permet aussi très rapidement de mettre au point +un nouvel environnement de test, totalement vierge. + +Cela réduit le frein au test, et augmente donc le nombre de tests et donc le +nombre de bugs détectés (et probablement aussi le nombre de bugs résolus). + +Plus on peut tester, plus on trouve de bugs, moins la qualité diminue. Écrire +un tel script prendra probablement quelques heures en début de projet, mais ces +heures "perdues" seront largement compensées par les heures perdues à mettre en +place des environnements, à tester ou ne pas tester car le setup est trop +pénible. + +Inutile d'en dire plus. + +Conclusion + +Selon les cas et les projets, nous n'appliquons pas nécessairement toutes ces +techniques. + +Un point à prendre également en compte et qui n'apparait pas ici : derrière +l'idée de réduire les temps de développement se cachent différents aspects. On +y trouvera naturellement des notions de coûts — moins de jours de développement +coûtent moins cher (c'est le cas initial à l'origine de ce journal), mais on y +trouve aussi des notions de time-to-market. Moins de temps de développement, +c'est une arrivée sur le marché plus rapide. Ce qui génère potentiellement des +gains différents. + +Le time-to-market… c'est notamment la raison pour laquelle pas mal de startup +fabriquent une première version en mode "mvp" voire carrément en mode "proto" : +cela permet de prendre les parts de marché initiales, quitte à complètement +redévelopper le produit par la suite — une fois que le marché a été pris, la +concurrence devient difficile, la startup est devenue rentable et les dépenses +de R&D nécessaires à la construction d'un système propre et robuste deviennent +envisageables. +Télécharger ce contenu au format Epub[19] + +Lire les commentaires[20] + +Liens: +[1]: http://linuxfr.org/journaux.atom#avant-propos (lien) +[2]: http://linuxfr.org/journaux.atom#introduction (lien) +[3]: http://linuxfr.org/journaux.atom#comment-r%C3%A9duire-les-temps-de-d%C3%A9veloppement (lien) +[4]: http://linuxfr.org/journaux.atom#r%C3%A9duction-de-temps-de-d%C3%A9veloppement-avec-perte-de-qualit%C3%A9 (lien) +[5]: http://linuxfr.org/journaux.atom#r%C3%A9duction-de-temps-de-d%C3%A9veloppement-sans-perte-de-qualit%C3%A9 (lien) +[6]: http://linuxfr.org/journaux.atom#quel-int%C3%A9r%C3%AAt (lien) +[7]: http://linuxfr.org/journaux.atom#quelques-techniques (lien) +[8]: http://linuxfr.org/journaux.atom#technique-n1--ne-pas-prendre-de-d%C3%A9cision-%C3%A0-la-place-du-client (lien) +[9]: http://linuxfr.org/journaux.atom#technique-n2--se-limiter-strictement-au-cahier-des-charges (lien) +[10]: http://linuxfr.org/journaux.atom#technique-n3--r%C3%A9duire-le-p%C3%A9rim%C3%A8tre-fonctionnel (lien) +[11]: http://linuxfr.org/journaux.atom#technique-n4--faire-du-test-driven-sur-les-interfaces (lien) +[12]: http://linuxfr.org/journaux.atom#technique-n5--faire-de-la-conception-progressive-sur-les-composants-internes (lien) +[13]: http://linuxfr.org/journaux.atom#technique-n6--diviser-pour-mieux-r%C3%A9gner (lien) +[14]: http://linuxfr.org/journaux.atom#technique-n7--%C3%89crire-du-code-simple (lien) +[15]: http://linuxfr.org/journaux.atom#technique-n8--%C3%89crire-un-script-de-setup-dun-environnement-fonctionnel (lien) +[16]: http://linuxfr.org/journaux.atom#conclusion (lien) +[17]: https://fr.wikipedia.org/wiki/Produit_minimum_viable (lien) +[18]: https://www.joelonsoftware.com/2000/08/09/the-joel-test-12-steps-to-better-code/ (lien) +[19]: https://linuxfr.org/users/lebouquetin/journaux/reduire-les-temps-de-developpement-sans-sacrifier-la-qualite.epub (lien) +[20]: https://linuxfr.org/users/lebouquetin/journaux/reduire-les-temps-de-developpement-sans-sacrifier-la-qualite#comments (lien) -- cgit v1.2.1