Plus propre, mais pareil ?
Un refactoring (une réécriture qui ne change pas le comportement) est l’une des tâches qu’on confie le plus volontiers à un assistant de code. Le résultat est plus court et plus joli.
Le risque est qu’il ne fasse plus la même chose, sans que rien ne casse à la vue. Cette leçon apprend à demander un refactoring par petits pas et à prouver, avec des chiffres, que rien n’a changé.
À la fin de cette leçon
Remarque
Je peux demander un refactoring à un assistant et prouver que le comportement n’a pas changé.
Mots-clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Refactoring | changer la forme du code sans changer ce qu’il fait | |
| Comportement | ce que le code rend pour chaque entrée | |
| Petit pas | un changement qu’on relit en une minute | |
| Entrée au bord | une valeur où une règle change | 9 et 10 |
| Avant et après | les sorties du code avant le changement et après |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Un refactoring se fait par petits pas
Demandez un seul rangement à la fois : remplacer une suite de if par une table, ou renommer une fonction. Plus le pas est petit, plus le diff se relit vite.
Dites aussi ce qui ne doit pas bouger : « ne change aucune sortie, n’ajoute aucune règle, ne corrige rien en passant ». Un diff qui range et qui corrige à la fois ne se relit pas.
Une demande possible
Voici une demande possible : « Dans remise.js, remplace la suite de if par une table des paliers. Ne change aucune sortie et ne touche à aucun autre fichier. Garde les tests tels quels. »
L’assistant peut répondre autrement d’une fois à l’autre. Ne regardez pas d’abord si le code est joli, mais si les sorties sont les mêmes.
La preuve est la comparaison avant et après
Avant le changement, notez ce que rend le code pour une liste d’entrées. Après, lancez la même liste et comparez ligne à ligne. Ce sont les mêmes valeurs, ou ce n’est pas un refactoring.
Choisissez surtout les entrées au bord de chaque règle. C’est là qu’un rangement se trompe le plus souvent.
Comparer deux versions en Python
def remise_avant(quantite):
if quantite >= 20:
return 15
if quantite >= 10:
return 10
if quantite >= 5:
return 5
return 0
PALIERS = [[20, 15], [10, 10], [5, 5]]
def remise_apres(quantite):
for palier in PALIERS:
if quantite >= palier[0]:
return palier[1]
return 0
# Essayez ensuite PALIERS = [[5, 5], [10, 10], [20, 15]] : que voyez-vous ?
for quantite in [0, 4, 5, 9, 10, 19, 20, 25]:
if remise_avant(quantite) != remise_apres(quantite):
print("difference pour", quantite)
print("comparaison finie")
Lancer la comparaison dans le labo
Les quatre Run suivants écrivent dans un dossier demo-refactor/ : le code d’avant, une version rangée, une version au piège et un script de comparaison. Ces versions sont écrites à la main, ce ne sont pas des réponses d’un assistant.
Le piège a une table qui a l’air identique, mais rangée dans l’autre sens. Voyons si la comparaison le voit.
Écrire le code d’avant
docker exec -w /home/node/project agent sh -c "mkdir -p demo-refactor && printf '%s\n' '// Prix de quantite articles, en centimes, avec une remise par paliers.' 'function remise(quantite) {' ' if (quantite >= 20) return 15;' ' if (quantite >= 10) return 10;' ' if (quantite >= 5) return 5;' ' return 0;' '}' 'function prix(quantite, centimes) {' ' const brut = quantite * centimes;' ' return Math.round(brut - brut * remise(quantite) / 100);' '}' 'module.exports = { prix };' > demo-refactor/avant.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire la version rangée
docker exec -w /home/node/project agent sh -c "mkdir -p demo-refactor && printf '%s\n' '// Meme calcul, avec les paliers dans une table du plus grand au plus petit.' 'const PALIERS = [[20, 15], [10, 10], [5, 5]];' 'function remise(quantite) {' ' for (const palier of PALIERS) {' ' if (quantite >= palier[0]) return palier[1];' ' }' ' return 0;' '}' 'function prix(quantite, centimes) {' ' const brut = quantite * centimes;' ' return Math.round(brut - brut * remise(quantite) / 100);' '}' 'module.exports = { prix };' > demo-refactor/apres.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire la version au piège
docker exec -w /home/node/project agent sh -c "mkdir -p demo-refactor && printf '%s\n' '// Une table qui a l’air identique, mais rangee du plus petit au plus grand.' 'const PALIERS = [[5, 5], [10, 10], [20, 15]];' 'function remise(quantite) {' ' for (const palier of PALIERS) {' ' if (quantite >= palier[0]) return palier[1];' ' }' ' return 0;' '}' 'function prix(quantite, centimes) {' ' const brut = quantite * centimes;' ' return Math.round(brut - brut * remise(quantite) / 100);' '}' 'module.exports = { prix };' > demo-refactor/piege.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire la comparaison
docker exec -w /home/node/project agent sh -c "mkdir -p demo-refactor && printf '%s\n' 'const avant = require(process.argv[2]).prix;' 'const apres = require(process.argv[3]).prix;' '// Les entrees aux bords de chaque palier, plus zero et une grande valeur.' 'const entrees = [0, 4, 5, 9, 10, 19, 20, 25];' 'let differences = 0;' 'for (const quantite of entrees) {' ' const a = avant(quantite, 150);' ' const b = apres(quantite, 150);' ' if (a === b) continue;' ' differences = differences + 1;' ' console.log(\"quantite \" + quantite + \" : \" + a + \" contre \" + b);' '}' 'console.log(differences + \" difference(s) sur \" + entrees.length + \" entrees\");' > demo-refactor/comparer.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Comparer l’avant à chaque version
docker exec -w /home/node/project/demo-refactor agent sh -c "node comparer.js ./avant.js ./apres.js; node comparer.js ./avant.js ./piege.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Une erreur fréquente
Attention
Beaucoup pensent que si les tests passent encore après le refactoring, rien n’a changé. Les tests ne couvrent que ce qu’ils vérifient. Comparez aussi les sorties sur les entrées au bord, avant et après : c’est la seule preuve que le comportement est le même.
Exemple travaillé : prix à 150 centimes
| Articles | Avant | Version rangée | Version au piège |
|---|---|---|---|
| 5 | 713 | 713 | 713 |
| 9 | 1283 | 1283 | 1283 |
| 10 | 1350 | 1350 | 1425 |
| 20 | 2550 | 2550 | 2850 |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Plus loin : essayer sur un autre changement
C’est le bon moment pour essayer ces idées sur un autre changement. Ne touchez pas au bogue du labo, qui est le travail de la leçon 6. Demandez plutôt de renommer ou de ranger une fonction de shop.js.
Notez d’abord les sorties de quelques entrées au bord, puis demandez le changement en petit pas, puis comparez. Gardez ce qui a changé, et dites si c’était demandé.