Download - Coder propre !
Coder propreaka Clean Code
Joseph Pachod https://twitter.com/josephonit
Credit: http://xkcd.com/292/
Précisions sur la présentation :
Coder propre est applicable à tous les langages
Pas d'atelier ou d'exemple précis, que des pistes : à chacun de parcourir le chemin.
Exemples en Java mais triviaux et à la portée de tout développeur.
“Make things as simple as possible — but no simpler.” Albert Einstein
“Complicated is not a sign that you are clever. It is a sign that you failed.” Gilad Bracha
“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” Martin Fowler
"Simplicity is a great virtue but it requires hard work to achieve it and education to appreciate it." Edsger Dijkstra
Pourquoi ?
Raisons "traditionnelles" :● On lit + le code qu'on ne l'écrit● Évolutivité de l'application● Développer la prochaine version plutôt que de corriger
l'actuelle
Raison réelle :Coder propre ou ne pas faire d'informatique !
Code sale :● Fragile● Développement laborieux, répétitif et aléatoire● Chaque évolution est une nouvelle galère● Délais et surprises pour la moindre modification (gestion
de projet peu agréable)● Expérience utilisateur mauvaise (régressions & bugs
fréquents)
=> coder propre voie de salut pour le développeur, le management et les utilisateurs
Comment ?
Faire complexe est facile, faire simple est difficile.
Savez vous coder propre ?
Coder sale n'apprend pas comment coder propre...
Besoin de culture informatique et de réflexion personnelle.
Quelques pistes, à vous de trouver le chemin :● Bonnes pratiques● Outils● Etat d'esprit
Attention !
Il s'agit de conseils, pas de dogmes religieux.
Chaque langage/framework/développeur a ses bonnes pratiques : se renseigner, discuter et se mettre d'accord avec les autres développeurs
Exemples de code à venir : mise en avant de points, pas code parfait...
Utilisation fréquence des termes « Foo » et « Bar », pour se concentrer sur le code et non le fonctionnel. Par exemple : public class Foo {
public Bar doBar() {return new Bar();
}}=> on ne sait rien du fonctionnel, on peut parler du code
public class BadComment {
private Bar var1 ;
public void foo() { // on teste l'état if( var1.state == null || var1.state == 0 /* foo.state == 1 || */ ) { //... } }}
Le premier commentaire dit ce que fait le test : aucune valeur ajoutée.
Noms et valeurs d'aucune aide : var1, state, entier 0 et 1... Génériques, interchangeables à volonté => vides de sens : on ignore de quel « state » on parle ainsi que le sens des valeurs testées
Qu'indique la ligne commentée ci dessous ? /* foo.state == 1 || */
La valeur 1 n'est plus présente ? La valeur 1 n'est plus à tester ?Quelqu'un a fait un test et a oublié de dé commenter ?=> soulève de nombreuses questions, alors qu'elle ne fait
rien !!=> gaspillage de ressources mentales
Mort aux commentaires !
Le moins possible de commentaires ● Signes d'un manque d'expressivité du code● Faciles à bâcler, dur à écrire proprement, quasi
impossible à maintenir - commentaires alors trompeurs et erronés● Ne PAS commenter le code modifié
- on a un gestionnaire de source pour cela
A réserver● Au code servant d'interface avec le monde extérieur
(API)● Pour expliciter des contraintes extérieures/contexte
- par ex. « initialisation précoce de ce composant car sinon XYZ passe avant (mal) » => toujours des « défaites »
Utiliser les commentaires visibles depuis les outils de génération de doc dès que possible
Refactoring
Actualiser le code existant● noms mis à jour● évolutions de la structure même si besoin
=> le code doit refléter l'état actuel des connaissances métier
Du code qu'on n'ose plus modifier est du code mort et de la dette technique : cela ira en empirant.
Pas de connaissance en dehors du code
final String CHRISTMAS_DAY = "25/12";final String NEW_YEAR_DAY = "01/01";final String FRENCH_NAT_DAY = "14/07";final String workersDay = "1/1";
Quelle ligne a ajouté le nouveau venu dans l'équipe ?
Respecter le style en vigueur
Uniformité du style => lecture plus aisée
Code résultat d'une action collective● par défaut, respecter le style en place● si besoin, discuter et décider en équipe du nouveau
style et des modalités de transition
Evite de se faire haïr... et accessoire : le style en vigueur n'a pas d'effet sur la qualité du code.
public class GoodComment {
private Bar bar;
public void foo() {
if (bar.isInProgress()) { // ... } }
}
Nommage explicite des classes, méthodes et variables
Pas de « bruit » inutile.
Bon niveau de détail : ● si on veut savoir comment est fait le test, on va voir
dans la méthode. ● Sinon on peut continuer à explorer le code actuel sans
surcharge cognitive.
Et en fait aucun commentaire :)
A. List<String> list = new ArrayList<String>();B. List<String> names = new ArrayList<String>();
C. if(bar.isNotDone() == false)D. if(bar.isDone())
=> B : dire le pourquoi, pas le comment. Le code dira toujours le comment, le pourquoi peut être impossible à déterminer s'il n'est pas indiqué...
=> D : penser à être facile à lire : nommer positivement est préférable à la double négation
Privilégier les méthodes courtes● Même une ligne est mieux
Exemple : statut == 0 vs isValide()● Petites méthodes bien plus réutilisables
Code auto descriptif
Les deux diapos précédant montrent du code qui s'explique de lui même
● Facilite la compréhension● Facilite la modification● Découpe le code et le structure
A la portée de tout le monde, suffit d'y penser !
Don't Repeat Yourself ( DRY )Aka Ne pas se répéter
(raté)
A appliquer partout & tout le temps● Tant pour le code que les constantes
Pas de duplication : un besoin qui change égal un endroit à modifier
● certitude de changer le bon morceau● pas de chasse épique aux doublons (parfois
fonctionnellement identiques mais écrits différemment dans le code : bonne chance pour les trouver)
● pas de surprise une fois en production « quand je fais ça ça ne marche toujours pas.. »
A. public int id;B. public Identifier<Good> id;
Cas A : les entiers sont communément utilisés : si vous voyez un entier quelque part, cela sera t il pour autant l'identifiant que vous cherchez ?
Si l'identifiant a des contraintes particulières, impossible de le voir avec un entier : besoin de regarder autour/en base pour comprendre les valeurs autorisées et le fonctionnement.
=> B : ● permet de voir tous les usages de l'identifiant en
question (via les références dans le code)● Permet d'aisément ajouter ou supprimer des
comportements ● N'importe où dans le code où ce type est présent, on
sait de suite de quoi il s'agit ou, au pire, il suffit d'aller voir la classe en question pour avoir le détail
Nommer et typer
Besoin spécifique => nom/classe spécifique
Exemple A précédent, un identifiant entier : peu clair, fragile, peu évolutif
Exemple B précédent, un type dédié : explicite, évolutif (ajouts aisés de comportements sans modifier le code existant)
Le type est :● le point unique pour les actions communes,● la documentation (auto descriptif donc).
public void doStuff() { // plein de choses faites puis... sendEmail("[email protected]", "Stuff done", "Details");}
private void sendEmail(String to, String subject, String text) { String from = "[email protected]"; String host = "smtp.acme.com"; Properties props = new Properties(); props.put("mail.smtp.host", host); Session session = Session.getInstance(props); Message msg = new MimeMessage(session); (...) Transport.send(msg);}
2 choses différentes au même endroit : le but initial de la classe, doStuff, et l'envoi de mail
Conséquences● Longueur de la classe● Détails et mises en œuvre très différents
Plein de problèmes latents :● Réutilisation difficile● Changements difficiles car plein de valeurs codées en
dur● Si le code de l'envoi de mail est copié ailleurs, sera t il
bien iso fonctionnel ?● Quid en cas d'erreur/exception lors de l'envoi du mail ?
Impacts sur doStuff() ?
=> mauvais
private final Mailer mailer;
public Good(Mailer mailer) { this.mailer = mailer;}
public void doStuff() { // plein de choses faites puis... mailer.sendEmail("[email protected]", "Stuff done", "Details");}
Rendre indépendant au maximum les différents modules les uns des autres
● le service d'envoi de mail ne doit faire que ça, sans dépendre ou appliquer des logiques relevant d'autres choses
● Pas de logique en fonction du contexte (utilisateur courant par exemple)
● Utilisation possible du service en dehors d'un contexte web
=> Augmente la réutilisation
Se concentrer sur l'essentiel d'une fonctionnalité permet de la traiter correctement
● Notamment le traitement des cas d'erreurs● En proposant différentes méthodes correspondant aux
différents besoins d'appels
=> meilleur
private final Notifier notifier;
public Open(Notifier notifier) { this.notifier = notifier;}
public void doStuff() { // plein de choses faites puis... notifier.notify(userAcme, "Details", "Stuff done");}
Bien réfléchir au nommage et au périmètre des services● L'envoi de mails n'est qu'une des façons de notifier● L'idée était de notifier, pas d'envoyer un mail● Permet de généraliser : on passe désormais
l'utilisateur et non plus directement l'adresse
Ne pas hésiter à séparer la déclaration de l'implémentation ● Initialement peut être que seul l'envoi de mail marche● Distingue bien le comment du pourquoi
=> encore meilleur
Découplage !
Exemples précédent : mise en œuvre de découplage● Une classe, une responsabilité● Augmente le niveau d'abstraction : plus pérenne
Principe aussi connu sous les noms d'orthogonalité ou de séparation des responsabilités (Separation of Concerns)
Maîtrisez votre IDE !
Votre IDE peut faire beaucoup, apprenez ses capacités, notamment :
● génération de code ● via des templates pour des éléments récurrents (le
logger par exemple)● en écrivant un code minimal, qui ne compile pas, et
en le laissant créer le reste● découverte/lecture du code
● liste des appelants● références● hiérarchie des classes
● propositions de refactoring● renommage & déplacements● création de méthode
● formatage automatique ● plus simple pour relire le code du collègue
Apprenez aussi ses raccourcis pour être efficace
Commencez par les tests !
Soyez le premier utilisateur de votre code via les tests● Avant même que le code ne soit écrit : permet de voir
si l'utilisation correspond aux attentes● Changer d'avis ne coute alors rien !
● Documente votre code : suffit de regarder les tests pour comprendre comment l'utiliser
Plusieurs niveaux de tests :● Tests unitaires : que pour un point précis (une classe,
une méthode)● Rapides à jouer, à exécuter à chaque changement
du code● Tests d'intégration : mettent en jeu plusieurs classes,
voir des ressources externes (base de données)● Lents ● Fragiles
Less is more
Moins il y a de code à maintenir, mieux c'est
Du code non fréquemment exercé tend à devenir rapidement obsolète
Mieux peu qui tourne que beaucoup qui plante
Code flexible != 2000 options de config
But : avoir du code simple facile à changer, pas du code complexe "qui fait tout" dur à modifier.
Les besoins de demain seront différents des prévisions d'aujourd'hui
Eviter les usines à gaz de paramétrage avec 2000 choix, mais (généralement) pas celui désiré
Si code propre, alors modification aisée.=> KISS (Keep It Simple Stupid)
Egoless programming
Critique du code != critique de soi
=> les discussions et critiques autour du code sont primordiales. Elles ne doivent pas être prises comme des attaques personnelles.
Etre accessible et prêt à discuter
Pensez !
Ne répliquez pas des fonctionnements sans les comprendre
Vous ne comprenez pas ? Des doutes ? Demandez, creusez !
Démarche cartésienne : ne corrigez pas la cause probable mais la causée avérée.=> des preuves, pas des soupçons ou de l'intuition
Faire propre de suite
Même pour un prototype ● qui peut évoluer● qui peut être repris● qui peut être utilisé par un collègue
Eviter de faire une mise en prod aujourd'hui et du bugfix pendant 10 ans...
Emergence naturelle de l'architecture=> Meilleure compréhension des besoins
Code == communication => il faut pouvoir montrer son code sans se sentir "sale"
Pas de raccourcis !
Formez vous !
Lecture● De livres, par exemple Clean code● De code "propre" -> nombreux projets Open Source (mais pas tous, souvent un signe de maturité, ex lucene, wicket, jquery)
Ateliers lors de conférence ● Qu'ils soient dédiés au code propre (tests...)● Ou sur un autre sujet : les échanges avec du code sont toujours très instructifs, tout comme voir un autre coder (et se servir de son environnement)
Coût du coder propre
Coût intellectuel● Remise en cause● Changement
Coût initial● Premières lignes codées propres laborieuses● Beaucoup de réflexions (salutaire)
Coût au quotidien● Léger surcoût au premier dév d'une fonctionnalité● Gain dès qu'on a besoin de relire, retoucher ou réutiliser
=> arrive généralement bien plus tôt qu'on ne le pense
Mieux vaut passer un peu plus de temps au début pour bien faire que de supporter ensuite longtemps quelque chose de mal fait !
Du (mauvais) code...
Immense majorité du code en entreprise non propre● Et même en pratique souvent très sale, voir terrifiant...
Le mauvais code n'apporte que très de satisfaction à ses auteurs : ce n'est pas plaisant !
Grande demande de code propre mais savoir qu'on fait du sale ne permet pas de savoir comment faire propre (du vécu).
Besoin et demande réels...Choisissez votre travail avec soin
Réelle attente de compétences=> Formez vous & pratiquez !
Certaines structures se moquent du code propre (et des développeurs) en général : évitez les si vous aimez coder.
Prendre le chemin du code propre est coûteux en temps et, surtout, en changements : il faut une vraie envie de la structure, pas un simple discours non étayé dans les faits.