Principes et idées clés du génie logiciel#
Cette page rassemble les principes et idées importantes qui traversent l’ensemble du cours. Chaque principe est traité en profondeur dans le module où il est le plus pertinent ; les liens ci-dessous pointent vers ces sections.
Gestion de la complexité#
- Complexité essentielle vs accidentelle (Fred Brooks, No Silver Bullet, 1986) → Module 3, Introduction, Module 6, Le développement assisté par IA
Principes de conception#
- Information hiding (David Parnas, 1972) : chaque module cache une décision de conception susceptible de changer → Module 3, Architecture et modularité
- DRY (Don’t Repeat Yourself, Hunt & Thomas, The Pragmatic Programmer, 1999) : chaque connaissance doit avoir une représentation unique → Module 3, Architecture et modularité
- KISS (Keep It Simple Stupid)
- YAGNI (You Ain’t Gonna Need It, Kent Beck, Extreme Programming) : ne construis pas d’abstraction pour un besoin qui n’existe pas encore → Module 3, Architecture et modularité
- Separation of Concerns : diviser un système en parties qui traitent chacune un aspect distinct du problème → Module 3, Architecture et modularité, Module 3, Les données (OLTP vs OLAP)
- SOLID (Robert C. Martin, Agile Software Development, 2003) : cinq principes de conception OO (S, O, L, I, D) → Module 3, Architecture et modularité
- Design patterns (Gang of Four, Design Patterns, 1994) : vocabulaire partagé de solutions récurrentes à des problèmes de conception ; plusieurs patrons classiques sont absorbés par les langages modernes → Module 3, Architecture et modularité
- Convention over Configuration (David Heinemeier Hansson, Rails, 2004) : préférer des conventions par défaut sensées à une configuration explicite, pour réduire les décisions répétitives → Module 3, L’architecture des applications web
- Law of Demeter
- Composition over inheritance (Gang of Four, Design Patterns, 1994) : favoriser l’assemblage d’objets plutôt que l’héritage de classes → Module 3, Architecture et modularité
- Loi de Conway (Melvin Conway, 1967) : la structure d’un système reflète la structure de communication de l’organisation qui le produit → Module 3, Architecture et modularité, Module 4, Scrum, Module 4, Gestion de projet
- Manœuvre de Conway inverse (LeRoy et Simons, 2010) : structurer délibérément les équipes pour obtenir l’architecture souhaitée → Module 4, Gestion de projet
- Principle of Least Astonishment (POLA)
- Principe du moindre privilège (Principle of Least Privilege) : chaque composant d’un système ne devrait avoir accès qu’aux ressources strictement nécessaires à sa tâche → Module 5, Est-ce que c’est sécuritaire ?
- Idempotence : une opération qu’on peut exécuter plusieurs fois avec le même résultat, propriété cruciale pour les APIs réseau → Module 3, Les APIs, Module 5, Comment je le déploie ?
- Immutable infrastructure : ne jamais modifier un artefact déployé, toujours le remplacer. Lien avec l’immutabilité en programmation fonctionnelle → Module 5, Comment je le déploie ?
Concurrence et parallélisme#
- La concurrence n’est pas le parallélisme (Rob Pike, 2012) : la concurrence est une manière de structurer un programme en tâches indépendantes, le parallélisme une manière de l’exécuter sur plusieurs coeurs. La première est une propriété du code, la seconde une propriété de la machine. Un programme découpé en dix tâches sur une machine à un seul coeur est parfaitement concurrent et n’a aucun parallélisme → Module 2, Pourquoi la concurrence, Module 2, Processus et threads
- Les threads pour attendre, les processus pour calculer : en Python, le verrou global (GIL) empêche deux threads d’exécuter du code simultanément, mais un thread qui attend le relâche. Les threads n’accélèrent donc jamais un calcul et accélèrent énormément une série d’attentes. La boucle d’événements est la deuxième réponse pour l’attente, et souvent la meilleure → Module 2, Processus et threads, Module 2, Ne jamais bloquer
- Préemption (par opposition au multitâche coopératif) : le système d’exploitation reprend la main sur un programme sans lui demander son avis, plutôt que de compter sur sa bonne volonté. Comme la protection mémoire, la garantie naît sur les gros systèmes des années 60 et met trente ans à atteindre les machines personnelles. Le multitâche coopératif revient pourtant par la petite porte, à l’intérieur d’un seul processus, sous le nom de boucle d’événements → Module 2, Processus et threads, Module 2, Ne jamais bloquer
- Ne jamais bloquer : dans une boucle d’événements, un seul cuisinier s’occupe de toutes les casseroles, et il change de casserole quand l’une sonne. La règle est absolue, ni calcul long ni attente synchrone, parce qu’un seul appel qui bloque fige tous les clients à la fois, personne ne pouvant pousser le cuisinier → Module 2, Ne jamais bloquer
- Écrire comme si on bloquait :
asyncetawaitn’ont rien changé à la boucle, seulement à la manière d’écrire ce qu’on lui confie. Fonctions de rappel, promesses, puis coroutines, trois visages du même modèle en quinze ans, de F# à C#, Python et JavaScript → Module 2, Ne jamais bloquer - Une boucle ne calcule pas plus vite : elle fait attendre sans coûter, elle ne fait pas calculer. Ce qui attend va dans une boucle ou des threads, ce qui calcule va dans des processus, un par coeur, qui ne partagent rien. La boucle, c’est de la concurrence sans parallélisme → Module 2, Ne jamais bloquer
- Ne pas partager d’état mutable : ce qui casse n’est pas le partage, c’est le partage d’une valeur que l’un lit pendant qu’un autre l’écrit. Deux stratégies, ne rien partager et s’échanger des messages, ou ne partager que des valeurs qui ne changent jamais → Module 2, Ne pas partager
- Ne communiquez pas en partageant la mémoire, partagez la mémoire en communiquant (Go, Andrew Gerrand, 2010) : une seule goroutine possède la donnée, les autres lui envoient des messages. Le poste de travail et le passe → Module 2, Ne pas partager
- Let it crash (Erlang, Joe Armstrong) : plutôt que de prévoir toute erreur, laisser mourir le processus fautif et le faire relancer par un superviseur. Ne fonctionne que parce que rien n’est partagé, et se retrouve dans Kubernetes, qui remplace un pod défaillant au lieu de le réparer → Module 2, Ne pas partager
- Encourager, interdire, ou prouver : les trois réponses des langages à la question de Lee. Go encourage à ne pas partager, Erlang l’interdit, Rust le fait vérifier par le compilateur → Module 2, Ne pas partager
- Condition de course (race condition) : quand le résultat d’un programme dépend de l’ordre dans lequel ses threads s’entrelacent, ordre que personne ne choisit. Le programme n’est pas faux, il est parfois faux, et ses tests passent → Module 2, Ce qui casse
- Interblocage (deadlock, Dijkstra, 1965) : deux threads qui tiennent chacun le verrou dont l’autre a besoin s’attendent pour toujours, sans planter ni rien signaler. La règle qui l’évite tient en une ligne, toujours prendre les verrous dans le même ordre, et elle est difficile à tenir parce qu’elle doit valoir pour tout le programme → Module 2, Ce qui casse
- On achète la correction avec de la vitesse : un verrou remet les threads en file indienne devant ce qu’il protège, et une file indienne n’est pas du parallélisme. Protéger le moins possible pour en perdre le moins possible → Module 2, Ce qui casse
- CPU-bound et I/O-bound : un programme est lent soit parce qu’il calcule, limité par le processeur, soit parce qu’il attend, limité par les entrées-sorties. Les deux se ressemblent vus du dehors et leurs remèdes sont opposés, des coeurs pour le premier, ne jamais bloquer pour le second → Module 2, Pourquoi la concurrence, Module 2, Ne jamais bloquer
- Loi d’Amdahl (Gene Amdahl, 1967) : la part d’un programme qu’on ne peut pas répartir fixe un plafond au gain du parallélisme. Avec 10 % de travail séquentiel, dix coeurs donnent un peu plus de cinq fois, et une infinité de coeurs jamais plus de dix → Module 2, Pourquoi la concurrence
- Loi de Moore (Gordon Moore, 1965) : le nombre de composants gravés sur une puce, à coût égal, double à intervalle régulier. Elle porte sur la quantité de transistors, et non sur la vitesse, contrairement à ce qu’on lui fait souvent dire → Module 2, Pourquoi la concurrence
- Loi de Dennard (Robert Dennard, 1974) : en réduisant les transistors, la puissance dissipée par unité de surface reste constante. C’est elle, et non celle de Moore, qui a cessé de s’appliquer vers 2004, forçant l’industrie à multiplier les coeurs plutôt que les cycles → Module 2, Pourquoi la concurrence
Débogage#
- Un bogue est un corps étranger, pas une faute morale (la mite du Mark II, 1947) : on le cherche comme on cherche un corps étranger, en ouvrant la machine et en regardant dedans → Module 2, Le débogage
- La réflexion attentive et quelques print judicieusement placés (Brian
Kernighan, 1979) : le
printest une sonde légitime. Ses trois limites, deviner où regarder, relancer à chaque question, retirer les sondes ensuite, sont exactement ce qu’un débogueur supprime → Module 2, Le débogage - Le débogueur est un print qu’on n’écrit pas : posé après coup sur n’importe quelle ligne, il montre toutes les variables à la fois, et la question suivante vient en lisant la réponse à la précédente, sans relancer → Module 2, Le débogage
- Un débogueur est une boucle interactive avec un contexte : l’invite
(Pdb)est le>>>de Python, arrêté à une ligne d’un programme en cours. Un notebook est la même boucle qui n’oublie rien, un point d’arrêt permanent → Module 2, Le débogage, Environnements du cours - Suivre les valeurs plutôt que relire le code : quand un total faux sort de quatre fonctions, on entre, on passe, on sort, et la fautive se révèle par ses valeurs, pas par sa lecture → Module 2, Le débogage
- Relancer le notebook de zéro avant de croire un résultat : l’état qui survit aux cellules exécutées dans le désordre produit des résultats que personne ne saura reproduire → Environnements du cours
Principes de systèmes distribués#
- Théorème CAP (Eric Brewer, 2000) : un système distribué ne peut garantir simultanément que deux des trois propriétés suivantes : cohérence, disponibilité, tolérance aux partitions → Module 5, Est-ce que ça va tenir la charge ?
- Premature optimization (Donald Knuth) : « Premature optimization is the root of all evil. » Ne pas concevoir pour des millions d’utilisateurs avant d’en avoir besoin → Module 5, Est-ce que ça va tenir la charge ?
Principes de mesure et d’observation#
- Loi de Goodhart (Charles Goodhart, 1975) : « Lorsqu’une mesure devient un objectif, elle cesse d’être une bonne mesure. » → Module 4, L’agilité (critique)
- Dette technique (Ward Cunningham, 1992) : le code imparfait livré consciemment est une dette qui génère des intérêts → Module 4, Gestion de projet
Principes de pratique#
- Refactoring (Martin Fowler, Refactoring, 1999) : modifier la structure interne du code sans changer son comportement observable, mécanisme de remboursement de la dette technique → Module 4, Gestion de projet
- Egoless programming (Gerald Weinberg, The Psychology of Computer Programming, 1971) : dissocier son ego de son code, accueillir les critiques comme des contributions → Module 2, Introduction
- Shift left : déplacer les vérifications de qualité le plus tôt possible dans le processus de développement, plutôt que de les confier à une équipe séparée en aval → Module 2, L’intégration continue
- Boy Scout Rule : “Always leave the campground cleaner than you found it.”