conférence #nwx2014 - maxime mauchaussée - partager du code maintenable et évolutif

Post on 27-Jun-2015

139 Views

Category:

Internet

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

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

top related