Le processus de développement
Aucun programme n'est terminé ni correct dès la première version. Entre l'idée de départ et le programme fiable, il y a un processus fait d'étapes qui se répètent : écrire, exécuter, tester, corriger. Maîtriser ce processus, et pas seulement écrire du code, est ce qui distingue un programmeur efficace. Dans ta spécialité NSI, tu le retrouveras dans chaque projet de groupe, avec ses points d'étape.
Le cycle de développement
Un bug est une erreur dans un programme ; déboguer, c'est la chercher, la comprendre et la corriger. La flèche « non » ramène à l'édition, car corriger, c'est modifier le code source.
Ces phases du développement d'un programme sont dans le désordre : (a) tester le programme avec différentes données, (b) éditer le code source, (c) déboguer les erreurs…
Ces phases du développement d'un programme sont dans le désordre : (a) tester le programme avec différentes données, (b) éditer le code source, (c) déboguer les erreurs trouvées, (d) exécuter le programme. Les remettre dans un ordre raisonnable et expliquer ce que l'on fait dans chacune, en tenant compte du fait que lors des tests il peut être nécessaire de revenir en arrière.
Expliquer ce qu'est un « bug » dans un programme et ce qu'est déboguer. Donner un exemple simple d'erreur que pourrait comporter le programme d'un distributeur…
Expliquer ce qu'est un « bug » dans un programme et ce qu'est déboguer. Donner un exemple simple d'erreur que pourrait comporter le programme d'un distributeur automatique de billets et la conséquence qu'elle aurait pour l'utilisateur.
La figure montre le cycle de travail du développement d'un programme, mais l'une de ses étapes est masquée par un point d'interrogation, comme le montre la figure.…
La figure montre le cycle de travail du développement d'un programme, mais l'une de ses étapes est masquée par un point d'interrogation, comme le montre la figure.
Indiquer quelle étape manque dans le cycle, en quoi elle consiste et pourquoi la flèche « non » ramène le travail à l'édition du code.
Les types d'erreurs
moyenne = (a + b + c / 3 # erreur de syntaxe : parenthèse non fermée
moyenne = (a + b + c) / 0 # erreur d'exécution : ZeroDivisionError
moyenne = (a + b + c) / 4 # erreur de logique : aucun message !
Distinguer une erreur qui apparaît à la compilation (le programme ne démarre même pas) d'une erreur qui apparaît à l'exécution (il démarre mais échoue avec certaines…
Distinguer une erreur qui apparaît à la compilation (le programme ne démarre même pas) d'une erreur qui apparaît à l'exécution (il démarre mais échoue avec certaines données), avec un exemple de chaque type.
Expliquer la différence entre une erreur de syntaxe et une erreur de logique, avec un exemple de chacune dans un programme qui calcule la moyenne de trois notes. Lequel…
Expliquer la différence entre une erreur de syntaxe et une erreur de logique, avec un exemple de chacune dans un programme qui calcule la moyenne de trois notes. Lequel de ces deux types empêche d'exécuter le programme et lequel permet de l'exécuter mais donne des résultats incorrects ?
Concevoir des jeux de tests
On peut aussi écrire les tests dans le code avec assert, qui arrête le programme si la condition est fausse :
def reduction(age):
"""Précondition : 0 <= age <= 130. Renvoie True si l'âge donne droit à la réduction."""
assert 0 <= age <= 130, "âge invalide"
return age >= 65
assert reduction(70) == True
assert reduction(65) == True # cas limite
assert reduction(64) == False
Expliquer ce qu'est un cas de test et écrire deux cas pour un programme qui additionne deux nombres : un avec des valeurs normales et un autre avec une valeur…
Expliquer ce qu'est un cas de test et écrire deux cas pour un programme qui additionne deux nombres : un avec des valeurs normales et un autre avec une valeur particulière, en indiquant l'entrée et le résultat attendu de chacun.
Un programme qui divise deux nombres fonctionne bien dans presque tous les tests, mais échoue lorsque le diviseur est 0. Expliquer ce que sont les valeurs limites ou cas…
Un programme qui divise deux nombres fonctionne bien dans presque tous les tests, mais échoue lorsque le diviseur est 0. Expliquer ce que sont les valeurs limites ou cas particuliers dans les tests, pourquoi il faut toujours les tester et citer deux autres valeurs avec lesquelles vous testeriez ce programme.
Pour tester un programme qui applique une réduction aux personnes de 65 ans ou plus, définir trois cas de test : un avec une valeur normale, un avec la valeur limite…
Pour tester un programme qui applique une réduction aux personnes de 65 ans ou plus, définir trois cas de test : un avec une valeur normale, un avec la valeur limite exacte et un avec une valeur erronée (âge négatif ou impossible), avec le résultat attendu de chacun. Justifier vos choix.
Un camarade d'équipe vous montre la fonction C++ suivante, qui calcule le prix final avec TVA et applique une remise si le client est adhérent, en affirmant : « Si elle…
Un camarade d'équipe vous montre la fonction C++ suivante, qui calcule le prix final avec TVA et applique une remise si le client est adhérent, en affirmant : « Si elle compile sans erreur ni avertissement, il est impossible qu'elle échoue à l'exécution ; inutile de la tester » :
float calculerPrixFinal(float base, bool estMembre) {
float total = base;
if (estMembre = true) {
total = total * 0.90f;
}
total = total * 1.21f;
return total;
}
-
Prévoir, en justifiant, la sortie de la fonction pour un client non adhérent (
estMembre = false) avec une base de 100,00 €. Cela correspond-il au comportement attendu ? Expliquer pourquoi le compilateur ne le rejette pas comme une erreur. -
À partir de cet exemple, justifier la différence entre ce que garantit la vérification syntaxique du compilateur et ce que démontrent les tests dynamiques d'un logiciel.
-
Proposer la correction du code et écrire un cas de test précis qui mettrait en évidence l'erreur si la correction n'était pas appliquée.
Concevoir un plan de tests pour un programme qui demande deux nombres et affiche leur division : réaliser un tableau avec au moins six cas de test (couples de valeurs…
Concevoir un plan de tests pour un programme qui demande deux nombres et affiche leur division : réaliser un tableau avec au moins six cas de test (couples de valeurs d'entrée, comprenant des valeurs normales, limites et erronées), le résultat attendu pour chacun et ce que le programme devrait faire si l'entrée n'est pas valide. Justifier le choix de chaque cas.
L'intranet du lycée met en œuvre un module d'authentification répondant aux exigences suivantes : Nom d'utilisateur : de 4 à 12 caractères alphanumériques, insensible à…
L'intranet du lycée met en œuvre un module d'authentification répondant aux exigences suivantes :
-
Nom d'utilisateur : de 4 à 12 caractères alphanumériques, insensible à la casse.
-
Mot de passe : entre 8 et 16 caractères, sensible à la casse, avec au moins un chiffre et un caractère spécial.
-
Sécurité : blocage temporaire du compte pendant 15 minutes après trois tentatives échouées consécutives.
Concevoir un plan de tests rigoureux sous forme de tableau comportant au moins six cas de test que vous aurez conçus. Pour chaque cas, préciser : l'identifiant, les valeurs d'entrée (nom d'utilisateur et mot de passe), l'état préalable du système, le résultat attendu et le risque précis de logique, de limite ou de sécurité que vous validez.
Un programmeur soutient qu'« une fonction d'une seule ligne n'a pas besoin de tests parce qu'elle est évidente » et présente cette fonction écrite en C++ : 1. Prévoir la…
Un programmeur soutient qu'« une fonction d'une seule ligne n'a pas besoin de tests parce qu'elle est évidente » et présente cette fonction écrite en C++ :
bool esImpar(int n) {
return (n % 2 == 1);
}
-
Prévoir la sortie de la fonction pour
n = 7,n = 0etn = -7. Expliquer techniquement pourquoi un résultat inattendu se produit pour l'un d'eux, en vous appuyant sur la façon dont l'opérateur modulo (%) traite les opérandes négatifs dans ce langage. -
Corriger l'expression pour que la fonction soit mathématiquement correcte avec n'importe quel nombre entier.
-
En vous appuyant sur ce cas, réfuter par deux arguments techniques l'idée de se passer de tests unitaires pour des algorithmes courts ou apparemment triviaux.
Les techniques de débogage
Expliquer ce qu'est un message de trace ou de journal (log) dans un programme et comment il aide à trouver une erreur (il permet de voir par où le programme est passé et…
Expliquer ce qu'est un message de trace ou de journal (log) dans un programme et comment il aide à trouver une erreur (il permet de voir par où le programme est passé et avec quelles valeurs).
Une boucle d'un programme ne s'arrête jamais. Expliquer où vous placeriez des messages d'affichage temporaires pour la diagnostiquer (dans la boucle, en affichant la…
Une boucle d'un programme ne s'arrête jamais. Expliquer où vous placeriez des messages d'affichage temporaires pour la diagnostiquer (dans la boucle, en affichant la variable de contrôle à chaque tour) et ce que vous observeriez dans ces messages pour trouver la cause.
Dans le système d'encaissement d'une coopérative scolaire, on a détecté que, lors de l'émission de la facture mensuelle, le total à payer de certains adhérents est…
Dans le système d'encaissement d'une coopérative scolaire, on a détecté que, lors de l'émission de la facture mensuelle, le total à payer de certains adhérents est erroné. Vous disposez des relevés suivants du système :
-
Adhérent 101 : 2 achats de 15,00 € -> Total encaissé : 30,00 € (attendu : 30,00 €).
-
Adhérent 102 : 1 achat de 50,00 € avec une remise promotionnelle de 10 % (5,00 €) et des frais de gestion de 3,00 € -> Total encaissé : 47,50 € (attendu : 48,00 €).
-
Adhérent 103 : 1 achat de 20,00 € et le retour d'1 article de 5,00 € (-5,00 €) -> Total encaissé : 25,00 € (attendu : 15,00 €).
-
Formuler une hypothèse justifiée sur l'erreur de logique ou d'ordre des opérations qui explique l'erreur pour l'adhérent 102 et pour l'adhérent 103.
-
Concevoir la stratégie de débogage pour confirmer vos hypothèses : indiquer quelles données de test minimales vous saisiriez pour isoler l'erreur, à quels endroits du déroulement du programme vous placeriez des points d'arrêt (breakpoints) et quelles variables précises vous devriez inspecter pas à pas.
Un script de simulation météorologique échoue environ une exécution sur vingt en levant une exception inattendue, mais lorsqu'on l'exécute manuellement pas à pas, il ne…
Un script de simulation météorologique échoue environ une exécution sur vingt en levant une exception inattendue, mais lorsqu'on l'exécute manuellement pas à pas, il ne reproduit jamais l'erreur (le programme utilise des requêtes à une API en temps réel et une fonction de génération de nombres aléatoires).
-
Justifier pourquoi cette erreur est classée comme non déterministe (heisenbug) et identifier les deux sources de variabilité qui empêchent de la reproduire à volonté.
-
Concevoir un plan d'action technique pour forcer la reproduction contrôlée de l'erreur sans attendre passivement qu'elle survienne au hasard (expliquer comment vous traiteriez le générateur de nombres pseudo-aléatoires et comment vous découpleriez les appels réseau externes).
-
Une fois le défaut localisé et corrigé, quelle procédure de test appliqueriez-vous pour certifier que l'erreur a complètement disparu et qu'il ne s'agit pas d'un simple hasard statistique ?
Modulariser, refactoriser et documenter
Les programmes ont des versions (1.0, 1.1, 2.0...). Expliquer à quoi sert de conserver des versions du code et ce que l'on peut faire si la dernière version fonctionne…
Les programmes ont des versions (1.0, 1.1, 2.0...). Expliquer à quoi sert de conserver des versions du code et ce que l'on peut faire si la dernière version fonctionne moins bien que la précédente.
La documentation accompagne le programme. Expliquer quelles deux choses elle devrait au minimum expliquer (ce que fait le programme et comment l'utiliser) et pourquoi un…
La documentation accompagne le programme. Expliquer quelles deux choses elle devrait au minimum expliquer (ce que fait le programme et comment l'utiliser) et pourquoi un programme non documenté « vieillit » mal lorsqu'une équipe y travaille.
Expliquer ce qu'est la modularisation d'un programme (le diviser en fonctions ou en modules) et analyser deux avantages qu'elle apporte lorsque le développement est…
Expliquer ce qu'est la modularisation d'un programme (le diviser en fonctions ou en modules) et analyser deux avantages qu'elle apporte lorsque le développement est réalisé par une équipe de plusieurs personnes : penser à la répartition du travail, aux tests et à la localisation des erreurs.
Expliquer ce qu'est refactoriser (améliorer le code sans changer ce qu'il fait) et décrire deux refactorisations typiques (renommer des variables avec des noms clairs,…
Expliquer ce qu'est refactoriser (améliorer le code sans changer ce qu'il fait) et décrire deux refactorisations typiques (renommer des variables avec des noms clairs, extraire une fonction à partir de code répété), avec un exemple de chacune.
Un fragment de code utilise les variables x, y2 et zzz pour stocker le prix, la TVA et le total. Réécrire ces noms de façon significative et expliquer pourquoi le choix…
Un fragment de code utilise les variables x, y2 et zzz pour stocker le prix, la TVA et le total. Réécrire ces noms de façon significative et expliquer pourquoi le choix des noms est un outil contre les erreurs (le code s'autodocumente).
Deux personnes d'une équipe éditent en même temps le même fichier du projet et, au moment de fusionner les modifications, un conflit apparaît. Expliquer ce qu'est un…
Deux personnes d'une équipe éditent en même temps le même fichier du projet et, au moment de fusionner les modifications, un conflit apparaît. Expliquer ce qu'est un conflit de fusion, comment on le résout (examiner les deux versions et décider) et quelles deux habitudes de travail en équipe permettent de le prévenir (répartir le travail par modules, intégrer les modifications souvent).
Le système de supervision d'une serre a été programmé dans un seul fichier, au sein d'une unique fonction de 400 lignes. Le code lit de façon séquentielle trois capteurs…
Le système de supervision d'une serre a été programmé dans un seul fichier, au sein d'une unique fonction de 400 lignes. Le code lit de façon séquentielle trois capteurs analogiques d'humidité du sol, calcule la moyenne, évalue des seuils, actionne une électrovanne via une broche numérique, enregistre l'historique dans un fichier texte et envoie un courriel d'alerte si l'humidité passe sous 30 %.
-
Justifier en détail trois problèmes techniques graves que cette architecture engendre lorsqu'il faut déboguer une erreur intermittente ou réaliser des tests unitaires automatisés.
-
Concevoir une refactorisation modulaire du programme : décomposer la fonction en au moins quatre fonctions indépendantes. Pour chacune, définir son nom, ses paramètres d'entrée avec leur type, sa valeur renvoyée et sa responsabilité unique.
-
Expliquer comment votre conception permettrait de tester l'algorithme de décision d'arrosage par simulation de données, sans avoir les capteurs physiques connectés à la carte.
Un groupe de trois élèves va développer pendant dix semaines une application de contrôle des présences pour le lycée. Pour éviter les retards et les livraisons ratées à…
Un groupe de trois élèves va développer pendant dix semaines une application de contrôle des présences pour le lycée. Pour éviter les retards et les livraisons ratées à la dernière minute, vous devez structurer la méthode de travail de l'équipe :
-
Concevoir un calendrier organisé en quatre jalons ou livrables vérifiables, en expliquant quels critères objectifs doivent être remplis avant d'autoriser le passage au jalon suivant.
-
Définir un protocole formel de relecture par les pairs (code review) : établir qui relit le code de qui, quelle liste de contrôle de qualité minimale doit être validée avant d'accepter toute modification et comment les désaccords sont gérés.
-
Établir et justifier une politique de « gel du code » (code freeze) : à quelle semaine elle doit être activée, quels types de modifications sont strictement interdits à partir de ce moment et quelles tâches exclusives l'équipe doit assumer jusqu'à la livraison finale.
Projet et équipe
Le programme de NSI réserve au moins un quart du temps à des projets menés en groupes de 2 à 4 élèves, avec des points d'étape réguliers. Le processus de développement s'y applique directement. Les objets connectés (capteurs qui mesurent, actionneurs qui agissent, communication par le réseau, application qui décide) sont un bon sujet de projet, et ils se testent comme tout programme : on découpe le projet en jalons vérifiables (par exemple : cahier des charges, première version qui fonctionne, tests complets, documentation et livraison), on se répartit les modules, et on relit le code les uns des autres avant d'intégrer. Avant de passer au jalon suivant, on exige un critère objectif : les tests du jalon passent tous.
Défi du thème. En tant qu'analyste logiciel junior, vous allez concevoir un script en Python (vous pouvez utiliser un environnement web gratuit comme Replit) qui évalue…
Défi du thème. En tant qu'analyste logiciel junior, vous allez concevoir un script en Python (vous pouvez utiliser un environnement web gratuit comme Replit) qui évalue le coût énergétique quotidien afin de promouvoir les économies d'énergie à la maison.
Suivre ces trois phases :
-
Recueil de données : Se rendre sur le site de RTE (le gestionnaire du réseau électrique) et relever le prix officiel ($€/\text{kWh}$) de $6$ tranches horaires différentes de la journée en cours.
-
Conception et codage : Appliquer la démarche de développement logiciel. Esquisser d'abord l'algorithme, puis le traduire en Python. Le programme doit stocker les données dans une liste, utiliser une boucle pour calculer le coût moyen quotidien et employer des instructions conditionnelles (
if-else) qui émettent des alertes lorsque le prix dépasse le seuil de $0,18$ €/kWh. -
Débogage et produit final : Réaliser des tests avec des valeurs limites et corriger les erreurs d'exécution. Rédiger un court rapport comprenant le code propre et commenté, une capture d'écran du résultat dans la console et deux propositions de consommation efficace pour votre foyer fondées sur vos données.
Seront évalués :
-
La rigueur dans la démarche de développement (conception préalable de l'algorithme, codage textuel et débogage).
-
L'utilisation correcte des structures de contrôle (séquentielles, conditionnelles et itératives) et de la syntaxe du langage.
-
La précision dans le traitement des données réelles et la pertinence des conclusions énergétiques.
Retour au début : un programme fiable n'est pas écrit d'un coup, il est construit par cycles successifs de tests et de corrections.