Conférence #nwx2014 - Maxime Mauchaussée - Partager du code maintenable et évolutif
-
Upload
normandie-web-xperts -
Category
Internet
-
view
139 -
download
0
Transcript of Conférence #nwx2014 - Maxime Mauchaussée - Partager du code maintenable et évolutif
Partager du codemaintenable et évolutif
Édition Débutant
{ PHP Style }
@wixiweb / @maximemdotnet
Les années 80 c'est terminé
Source : https://secure.flickr.com/photos/flysi/2586786319/in/photostream/
2014
Fini les nerd qui développent des application à eux tous seul
Entreprise IT / Système d'information / R&D
Freelance / Prestation
Projet open source / communautaire
Les technologies évoluent
L'électronique et l'informatique
Les techniques et langages de programmation
Les services
Les utilisateurs
Internet est omniprésent
Smartphone, tablette, PC, objets connectés
Tout le monde veut être présent
Se démarquer demande de + en + de ressources
Les utilisateurs sont de + en + exigeants
Problématiques
Source : https://secure.flickr.com/photos/watz/329055725/in/photostream/
Collaboration vs. Autonomie
Travailler en équipe sans interférer sur les tâches de chacun
Contrôler le code sans être derrière chaque développeur
Intégration de ressource / équipe distante ou télétravail
Être capable de reprendre le code d'un autre développeur
Rapidité vs. Réflexion
Les demandes clients et l'environnement changent
Il faut être réactif et compétitif face à la concurrence
Le manque de visibilité favorise les mauvais choix
Dette technique
Complexité vs. Compréhension
Les architectures sont de + en + complexes
Au fur et à mesure du temps le code devient + complexe
Un code complexe est difficile à maintenir et à changer
La complexité nécessite d'avoir plus de compétences
Pérennité vs. Péremption
Le code a une date de péremption, 3 à 5 ans
Les langages évoluent : amélioration, dépréciation, cassure
Les systèmes d'exploitation évoluent
En tant que développeur, vous évoluez
Solutions
Source : https://secure.flickr.com/photos/puuikibeach/3192430591/in/photostream/
10 trucs & astuces simples et rapides
10 conseils facilement applicables, dès demain !
Centrés sur le travail en équipe et la maintenabilité
Pas besoin d'être un Dieu du PHP pour les utiliser
À peaufiner au quotidien dans votre travail
Disclaimer
Conseils != Règles absolues
Je n'ai pas la prétention de vous apprendre à coder
Mettez en place une organisation à votre échelle
Appliquez le principe du « Close Enough »
Travailler en équipe
1. Outil de gestion de versions
git, subversion, mercurial
Travailler à plusieurs sur le même code source
Garder un historique et faire des versions de son application
Faire des évolutions et des bugfixes en parallèle
1. Outil de gestion de versions
Source : http://nvie.com/posts/a-successful-git-branching-model/
2. Utiliser un IDE
Netbeans, PhpStorm, vim
Environnement de Développement Intégré
Regroupe des outils pour coder plus rapidement / facilement
Utiliser le même IDE au sein d'une équipe
3. Utiliser une convention de codage
PEAR, Zend, PSR-1, PSR-2
Permet d'avoir un code source cohérent et une meilleure prise en main par une
personne qui ne l'aurait pas écrit
Les conventions de codage sont faites pour améliorer la lisibilité et éviter les
erreurs bêtes
Évite les débats stériles
PSR-*
PHP Framework Interoperability Group (PHP-FIG)
PSR = PHP Specification Request
Une recommandation pour utiliser PHP de manière interopérable
http://www.php-fig.org/
PSR-*
PSR-0 : Standard pour l'Autoloading
PSR-1 : Convention de codage de base
PSR-2 : Convention de style
PSR-4 : Amélioration du standard pour l'Autoloading
PHP_CodeSniffer
https://github.com/squizlabs/PHP_CodeSniffer
Parse le code PHP pour détecter les violations d'une convention
de codage définie
Supporte les conventions de codage les + populaires
Tester la compatibilité avec une version de PHP donnée
PHP_CodeSniffer
$ phpcs /path/to/code/myfile.php
FILE: /path/to/code/myfile.php--------------------------------------------------------------------------------FOUND 5 ERROR(S) AND 1 WARNING(S) AFFECTING 5 LINE(S)-------------------------------------------------------------------------------- 2 | ERROR | Missing file doc comment 20 | ERROR | PHP keywords must be lowercase; expected "false" but found | | "FALSE" 47 | ERROR | Line not indented correctly; expected 4 spaces but found 1 47 | WARNING | Equals sign not aligned with surrounding assignments 51 | ERROR | Missing function doc comment 88 | ERROR | Line not indented correctly; expected 9 spaces but found 6--------------------------------------------------------------------------------
PHP_CodeSniffer
4. Partager un environnement de développement commun
Développer sur un environnement similaire à la production
Système d'exploitation, serveur web, version de PHP, SGBD
Une machine (ou un vhost) par développeur et par projet
Utiliser une solution de virtualisation : docker, proxmox, vagrant
Pourquoi produire du code maintenable ?
Source : https://xkcd.com/844/
Deux exemples concrets récents
Le firmware ECM de Toyota
=> Un mort
Le bug SSL d'Apple
=> Du troll à foison
if ((err = SSLFreeBuffer(&hashCtx)) != 0) goto fail;
if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) != 0) goto fail;if ((err = SSLHashSHA1.update(&hashCtx, &clientRandom)) != 0) goto fail;if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) goto fail;if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail; goto fail;if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0) goto fail;
err = sslRawVerify(ctx, ctx->peerPubKey, dataToSign, /* plaintext */ dataToSignLen, /* plaintext length */ signature, signatureLen);if(err) {
sslErrorLog("SSLDecodeSignedServerKeyExchange: sslRawVerify " "returned %d\n", (int)err);
goto fail;}
fail: SSLFreeBuffer(&signedHashes); SSLFreeBuffer(&hashCtx); return err;}
Source : https://opensource.apple.com/source/Security/Security-55471/libsecurity_ssl/lib/sslKeyExchange.c?txt
if ((err = SSLFreeBuffer(&hashCtx)) != 0) goto fail;
if ((err = ReadyHash(&SSLHashSHA1, &hashCtx)) != 0) goto fail;if ((err = SSLHashSHA1.update(&hashCtx, &clientRandom)) != 0) goto fail;if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) goto fail;if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail; goto fail;if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0) goto fail;
err = sslRawVerify(ctx, ctx->peerPubKey, dataToSign, /* plaintext */ dataToSignLen, /* plaintext length */ signature, signatureLen);if(err) {
sslErrorLog("SSLDecodeSignedServerKeyExchange: sslRawVerify " "returned %d\n", (int)err);
goto fail;}
fail: SSLFreeBuffer(&signedHashes); SSLFreeBuffer(&hashCtx); return err;
}
Le code historique
Legacy code
Code présent avant vous, développé par quelqu'un d'autre
Principe du « touche pas le truc », inchangé depuis des années
Souvent fouillis, complexe, difficilement lisible
Pourquoi détestons-nous le code d'autrui ?
Le code nous paraît complexe
Manque de visibilité sur les choix faits
Syndrôme NIH : Not Invented Here
Avec le temps on déteste notre propre code
Mesurer la complexitéWTF / minute
Mesurer la complexitéComplexité Cyclomatique
Métrique pour mesurer la complexité logique
~ Représente le nombre de conditions + boucles dans le code
Plus de conditions équivaut à moins de compréhension
Le cerveau doit être attentif à tous les points de décisions
class Foo { 1 public function example() { 2 if ($a == $b) { 3 if ($a1 == $b1) { fiddle();4 } else if ($a2 == $b2) { fiddle(); } else { fiddle(); }5 } else if ($c == $d) { 6 while ($c == $d) { fiddle(); }7 } else if ($e == $f) { 8 for ($n = 0; $n > $h; $n++) { fiddle(); } } else{ switch ($z) {9 case 1: fiddle(); break;10 case 2: fiddle(); break;11 case 3: fiddle(); break;12 default: fiddle(); break;}}}}Source : http://phpmd.org/rules/codesize.html
Mesurer la complexitéComplexité N-Path
Représente le nombre de chemins uniques possibles
= nombre de tests nécessaires pour couvrir 100 % des cas
Plus de chemins égale moins de compréhension
Le cerveau doit garder en tête toutes les solutions possibles
Mesurer la complexitéComplexité N-Path
function example($a, $b) { $c = 0; if ($a < $b) { $c = $a; } elseif ($a > $b) { $c = $b; } if ($c < 2) { $c = $a + $b; } return $c;}
Mesurer la complexitéComplexité N-Path
Les seuils idéaux
CC pour une méthode : de 1 à 4
CC moyenne par méthodes : de 1 à 2
CC sur le nombre total de lignes de code : de 0.01 à 0.05
N-Path pour une méthode : de 1 à 16
PHP Mess Detector
http://phpmd.org/
Calcul la complexité d'un code source selon plusieurs indicateurs
Indique l'utilisation de fonctions controversées
Détecte le code mort
PHP Mess Detector
Produire du code maintenable
Qu'est ce qu'un code maintenable ?
Lisible
Compréhensible
Réutilisable
Testable
La meilleure façon d'avoir un code maintenable
Source : https://secure.flickr.com/photos/f-oxymoron/9647972522/in/photostream/
La meilleure façon d'avoir un code maintenable
Ne pas l'écrire
Source : http://whynne.deviantart.com/art/Comic-Trolls-98357844
La meilleure façon d'avoir un code maintenable
Laisser quelqu'un d'autre coder et corriger à sa place
Ne pas réinventer la roue
L'utilisation de code produit par d'autres est instructif
Attention : ne pas confondre avec le copier / coller !
5. Utiliser un (bon) framework
Symfony 2, Zend Framework 2, Silex, Aura, Phalcon
Communauté active et ouverte
Des fondateurs / mainteneurs reconnus
Tirant partie des dernières fonctionnalités de PHP
5. Utiliser un (bon) framework
Symfony 2, Zend Framework 2, Silex, Aura, Phalcon
Communauté active et ouverte
Des fondateurs / mainteneurs reconnus
Tirant partie des dernières fonctionnalités de PHP
Composer
Gestionnaire de dépendances pour PHP
Installe et met à jour les librairies tierces utilisées dans vos projets
Possibilité de télécharger les librairies depuis plusieurs sources :
packagist.org par défaut
Génère un fichier pour gérer l'autoload dans vos projet
Composer
composer.json
{ "require": { "phpmd/phpmd": "~1.5" }}
Composer
$ composer.phar install
Loading composer repositories with package informationUpdating dependencies (including require-dev) - Installing symfony/filesystem (v2.5.0) Downloading: 100%
- Installing symfony/config (v2.5.0) Downloading: 100%
- Installing symfony/dependency-injection (v2.5.0) Downloading: 100%
- Installing pdepend/pdepend (2.0.0) Downloading: 100%
- Installing phpmd/phpmd (1.5.0) Downloading: 100%
symfony/dependency-injection suggests installing symfony/yaml ()symfony/dependency-injection suggests installing symfony/proxy-manager-bridge (Generate service proxies to lazy load them)Writing lock fileGenerating autoload files
Produire du code maintenable (en vrai)
Source : https://twitter.com/dr4goonis/status/476617165463105536
Objectifs
Écrire le moins de code possible
Réduire la complexité
Augmenter la ré-utilisabilité
Pouvoir écrire des tests rapidement
Les 5 principes SOLID
S Single responsibility Responsabilité unique
O Open/closed Ouvert/fermé
L Liskov Substitution Substitution de Liskov
I Interface Segregation Ségrégation des interfaces
D Dependency Inversion Principle Inversion des dépendances
6. Factoriser son code
20 lignes maximum par méthode
200 lignes maximum par classe
10 méthodes maximum par classe
15 classes maximum par package
7. Un seul niveau d'indentation par méthode
Pas de condition / boucle imbriquée
Tirer partie des fonctions natives de PHP, + rapides, + lisibles
Responsabilité unique, - complexité, + ré-utilisabilité
Mode ninja : pas de « else »
7. Un seul niveau d'indentation par méthode
Early return / Guard clauses
Extraire les conditions dans des méthodes : isValid()
PHP propose ~80 fonctions natives pour manipuler les tableaux
D'autres pratiques :
http://sourcemaking.com/refactoring/simplifying-conditional-expressions
7. Un seul niveau d'indentation par méthode
8. Pas d’abréviation !
Utiliser des noms de variable et méthode qui ont du sens (in english !)
Penser le nommage comme des Lego
Éviter les noms génériques : $index, $key, $value, $object, etc.
Nom de variable / méthode trop long et dupliqué partout ?
→ Trop de responsabilités, pas assez de factorisation
$temp = array();for ($i = 0; $i < strlen($title); $i++){ $ordinal = ord($title[$i]);
if ($ordinal < 128) { $x[] = "|".$ordinal; } else { if (count($temp) == 0) { $count = ($ordinal < 224) ? 2 : 3; }
$temp[] = $ordinal; if (count($temp) == $count) {
$number = ($count == 3) ? (($temp['0'] % 16) * 4096) + (($temp['1'] % 64) * 64) + ($temp['2'] % 64) : (($temp['0'] % 32) * 64) + ($temp['1'] % 64); $x[] = "|".$number; $count = 1; $temp = array();
} }}
Source : CodeIgniter 2.2.0
8. Pas d’abréviation !
Source : http://trollcats.com
8. Pas d’abréviation !
function findUserByEmail($emailAddress) {}
function write(PostTitle $postTitle, PostContent $postContent);
$pageCount = ceil(count($userCollection) / $entityPerPage);
$addressLines = explode(PHP_EOL, $address);
8. Pas d’abréviation !
9. Lisibilité
Morbi molestie, elit vitae consectetur adipiscing, orci nulla hendrerit risus, ac vehicula orci eros sed leo. Fusce auctor ut ipsum non pharetra. Nunc at lorem gravida, scelerisque neque sed, lacinia ipsum! Aenean malesuada felis quam, fermentum fermentum elit viverra ut! Ut a orci sapien. Nulla dictum leo et tortor sollicitudin tempus. Aliquam lacinia, quam in condimentum aliquet, nisi augue feugiat ligula; eget cursus felis est sit amet augue. Morbi pulvinar pharetra cursus? Mauris feugiat lectus vel felis bibendum dignissim. In hac habitasse platea dictumst. Phasellus id consectetur magna!
9. Lisibilité
Morbi molestie, elit vitae consectetur adipiscing, orci nulla hendrerit risus, ac vehicula orci eros sed leo.
Fusce auctor ut ipsum non pharetra.
Nunc at lorem gravida, scelerisque neque sed, lacinia ipsum!
Aenean malesuada felis quam, fermentum fermentum elit viverra ut!
Ut a orci sapien. Nulla dictum leo et tortor sollicitudin tempus.
Aliquam lacinia, quam in condimentum aliquet, nisi augue feugiat ligula; eget cursus felis est sit amet augue.
9. Lisibilité
Penser le code comme du texte
Faire des paragraphes, aérer le code
Faire le moins de colonnes possibles
Une seule par ligne→
9. Lisibilité$serviceManager = $this->serviceManager;$events = $this->events;
$listeners = array_unique(array_merge($this->defaultListeners, $listeners));
foreach ($listeners as $listener) { $events->attach($serviceManager->get($listener));}
// Setup MVC Event$this->event = $event = new MvcEvent();$event->setTarget($this);$event->setApplication($this) ->setRequest($this->request) ->setResponse($this->response) ->setRouter($serviceManager->get('Router'));
// Trigger bootstrap events$events->trigger(MvcEvent::EVENT_BOOTSTRAP, $event);return $this;
Source : Zend Framework 2.3.1
9. Lisibilitépublic function someVeryLongMethodName(ObjectType $firstArgument, ObjectType $secondArgument, ObjectType $thirdArgument){ // ...}
public function someVeryLongMethodName( ObjectType $firstArgument, ObjectType $secondArgument, ObjectType $thirdArgument){ // ...}
$longVariableName = array($someKey => $someValue, $anotherKey => $anotherValue);
$longVariableName = array( $someKey => $someValue, $anotherKey => $anotherValue);
9. Lisibilité
$this->context->customer->firstname.' '.$this->context->customer->lastname
$this->getCustomer() ->getFullname();
Source : Prestashop 1.6.0.8
10. Documenter son code
Utiliser le (presque) standard PHPDoc
Ajouter un DocBlock au début de chaque méthode
1 à 2 lignes de commentaires au début de chaque paragraphe
Auto-complétion / Documentation dans l'IDE
10. Documenter son code
// Si le formulaire est valideif ($form->isValid() === true) {
}
// Retourne l'utilisateurreturn $user;
//When I wrote this, only God and I understood what I was doing//Now, God only knows
10. Documenter son code
/** * Bootstrap the application * * Defines and binds the MvcEvent, and passes it the request, response, and * router. Attaches the ViewManager as a listener. Triggers the bootstrap * event. * * @param array $listeners List of listeners to attach. * @return Application */public function bootstrap(array $listeners = array())
Source : Zend Framework 2.3.1
10. Documenter son code
// Send a challenge in each acceptable authentication scheme$headers = $this->response->getHeaders();if (in_array('basic', $this->acceptSchemes)) { $headers->addHeaderLine($headerName, $this->_basicHeader());}if (in_array('digest', $this->acceptSchemes)) { $headers->addHeaderLine($headerName, $this->_digestHeader());}return new Authentication\Result( Authentication\Result::FAILURE_CREDENTIAL_INVALID, array(), array('Invalid or absent credentials; challenging client'));
Source : Zend Framework 2.3.1
Récapitulatif
1. Utiliser un outil de gestion de versions
2. Utiliser un IDE
3. Utiliser une convention de codage
4. Partager un environnement de développement commun
5. Utiliser un framework / du code produit par d'autres équipes
6. Factoriser son code : 20 lignes de code maximum par méthode
7. 1 niveau d'indentation maximum par méthode
8. Pas d'abréviation dans les noms de variables et méthodes
9. Améliorer la lisibilité de son code en formant des paragraphes
10.Documenter son code
Prochaines étapes
Écrire du code propice à l'évolution
Tester son code
Intégration continue
Passer Paladin niveau 7
Entraînez-vous :)
Questions ?
Merci de votre attention :)
@wixiweb / @maximemdotnet