Slide 1

Slide 1 text

foolab.ca | @foolabca Réécriture de code vieux de 15 ans PHP Québec, Montréal - 7 juillet, 2016

Slide 2

Slide 2 text

Anna Filina • Développement. • Résolution de problèmes. • Formation. • Conseil stratégique. • FooLab + ConFoo. 2

Slide 3

Slide 3 text

Vous avez hérité
 d'une application de 10 ans

Slide 4

Slide 4 text

Vous avez hérité
 d'une application de 12 ans

Slide 5

Slide 5 text

Vous avez hérité
 d'une application de 15 ans

Slide 6

Slide 6 text

Vous avez hérité
 d'une application de 15 ans Le code ne vieillit pas comme le vin

Slide 7

Slide 7 text

Le web était 600px de large 7

Slide 8

Slide 8 text

Il y a 15 ans • Bug Y2K. • Google lance AdWords. • Pas de Twitter, Facebook, ou iTunes. • Internet Explorer 5. • Coins arrondis = marque de statut. 8

Slide 9

Slide 9 text

Objectifs • Réduire les erreurs. • Augmenter la vitesse de dev. • Réduire la dette technique. • Conseils. 9

Slide 10

Slide 10 text

Mon expérience legacy

Slide 11

Slide 11 text

Fichier legacy moyen • 3000-6000 lignes de code. • La moitié est commentée "juste au cas". • Méthodes de 800 lignes en moyenne. • Abus de classes "helper". • Souvent pas de classes. 11

Slide 12

Slide 12 text

Code applicatif • 5,000 classes. • 20,000 méthodes. • 1,500,000 lignes de code. 12

Slide 13

Slide 13 text

Prendre le code par les cornes

Slide 14

Slide 14 text

Duplication de code • Étape 1! • Parfois, un bogue est répété 80+ fois. • Réutiliser le code et éliminer les duplications. 14

Slide 15

Slide 15 text

Méthodes trop longues (800 lignes) • Extraire la partie brisée dans une méthode séparée. • Écrire les tests pour cette méthode. • Corriger le bogue. • Appeller à partir de la méga-méthode. 15

Slide 16

Slide 16 text

Fausse sécurité 16

Slide 17

Slide 17 text

Fausse sécurité $clean = htmlentities($_POST['id']); 16

Slide 18

Slide 18 text

Fausse sécurité $clean = htmlentities($_POST['id']); // utilisez les fonctions "filter" ou un framework $clean = filter_var($_POST['id'], FILTER_SANITIZE_NUMBER_INT); 16

Slide 19

Slide 19 text

Fausse sécurité $clean = htmlentities($_POST['id']); // utilisez les fonctions "filter" ou un framework $clean = filter_var($_POST['id'], FILTER_SANITIZE_NUMBER_INT); // utilisez les requêtes préparées $stmt = $pdo->prepare( "SELECT * FROM user WHERE id = :id"); 16

Slide 20

Slide 20 text

Fausse sécurité $clean = htmlentities($_POST['id']); // utilisez les fonctions "filter" ou un framework $clean = filter_var($_POST['id'], FILTER_SANITIZE_NUMBER_INT); // utilisez les requêtes préparées $stmt = $pdo->prepare( "SELECT * FROM user WHERE id = :id"); $stmt->bindParam(':id', $clean); $stmt->execute(); 16

Slide 21

Slide 21 text

Qu'est-il arrivé à la commande 20117? 17

Slide 22

Slide 22 text

Qu'est-il arrivé à la commande 20117? if ($order_id > 20117) { // utiliser cet sql } else { // utiliser cet sql } 17

Slide 23

Slide 23 text

Qu'est-il arrivé à la commande 20117? if ($order_id > 20117) { // utiliser cet sql } else { // utiliser cet sql } // réparez les données précédentes 17

Slide 24

Slide 24 text

Mélange de HTML, PHP et includes 18

Slide 25

Slide 25 text

Mélange de HTML, PHP et includes 18

Slide 26

Slide 26 text

Mélange de HTML, PHP et includes 19

Slide 27

Slide 27 text

Mélange de HTML, PHP et includes getTransactions(); ?> 19

Slide 28

Slide 28 text

Trop d'arguments 20

Slide 29

Slide 29 text

Trop d'arguments protected function getMock($originalClassName, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false, $callOriginalMethods = false, $proxyTarget = null) { 20

Slide 30

Slide 30 text

Trop d'arguments 21

Slide 31

Slide 31 text

Trop d'arguments $mock = $this->getMockBuilder($originalClassName) ->disableOriginalConstructor() ->disableOriginalClone() ->disableArgumentCloning() ->disallowMockingUnknownTypes() ->getMock(); 21

Slide 32

Slide 32 text

Préparation 22

Slide 33

Slide 33 text

Préparation • Besoin de logging. ◦ Monolog si la version PHP le permet. ◦ Au pire: file_put_contents. 22

Slide 34

Slide 34 text

Préparation • Besoin de logging. ◦ Monolog si la version PHP le permet. ◦ Au pire: file_put_contents. • Besoin de tests. ◦ Mocker les méthodes statiques: Patchwork ou équivalent. ◦ Caching des appels API. 22

Slide 35

Slide 35 text

Stratégie • Élaborez une stratégie autour des contraintes. • Réécriture complète ou progressive. ◦ Par fichier/classe. ◦ Par module. ◦ Par appel HTTP. 23

Slide 36

Slide 36 text

Examples contraintes ⟶ stratégie

Slide 37

Slide 37 text

PHP 3 à PHP 5.6 • HTML + PHP + SQL dans le même fichier. • Des includes partout. • IFs qui concatènent du SQL. • Tentative de réécriture antérieure. ◦ Échec, rendu pire. ◦ Dossiers de code mort. ◦ Du mauvais code écrit de deux façons. ◦ Aucune notion d'orienté-objet. 25

Slide 38

Slide 38 text

Solution • Réécrire formulaires complexes en Symfony 2. ◦ Approche: appel HTTP. ◦ mod_rewrite pour les pages concernées. • Réécrire le module le plus brisé en orienté-objet. ◦ Approche: module. ◦ Extraction de conception. ◦ Test unitaires. ◦ Architecture flexible. 26

Slide 39

Slide 39 text

Extraction de conception

Slide 40

Slide 40 text

Éviter le biais du code • Ancien code → conception. • Valider la conception. ◦ Clarifier les règles d'affaires. • Améliorer la conception. ◦ Réduire dette technique. ◦ Plus flexible. • Conception → nouveau code. 28

Slide 41

Slide 41 text

Avant de coder....

Slide 42

Slide 42 text

Les données se perdent, les choses se brisent • Sauvegarde: tester la restauration. • Environment staging: versions d'app, configurations. • Simuler déploiements/upgrades. • Automatiser des tests avant les changements. • Évaluer le risque. ◦ Ne soyez pas trop optimistes. ◦ Considérez les effets secondaires: coût, temps de réparation. 30

Slide 43

Slide 43 text

D'autres exemples

Slide 44

Slide 44 text

ASP Classic à PHP 5.6 • Spaghetti et hacks de 15+ ans. • Le language n'est plus supporté. • Gros ERP avec beaucoup de code. 32

Slide 45

Slide 45 text

Solution • Réécriture dans Symfony 2, page par page. • mod_rewrite pour les pages concernées. • Adapteur de session dans BD dans les deux apps. • Page dans n'importe quel langage = requête HTTP ◦ Tests Guzzle. 33

Slide 46

Slide 46 text

Tests Guzzle 34

Slide 47

Slide 47 text

PHP 5.3 à PHP 5.6 • Spaghetti et hacks de 12+ ans. • Utilise des fonctions obsolètes. • Ne peut pas rouler sur PHP 5.6. 35

Slide 48

Slide 48 text

Solution • Séparer en serveurs 5.3 et 5.6 pour un nouveau départ. • REST et design patterns modernes. • Frontend en AngularJS. 36

Slide 49

Slide 49 text

Séparer les serveurs 37

Slide 50

Slide 50 text

Alias est votre ami (5.3 server) Alias "/module-name" "/var/www/project/angular" RewriteBase /module-name/ 38

Slide 51

Slide 51 text

Coincé?

Slide 52

Slide 52 text

Essayez du nouveau • Échangez des idées. ◦ Nouvelles personnes qui ne connaissent pas le projet. • Est-ce que ça a été fait avant? • Essayez une approche complètement différente. 40

Slide 53

Slide 53 text

Demandez aux experts en legacy • Posez des questions. • Lisez leur blogue. • Embauchez-en au pour une journée de conseils. ◦ Stratégie de refactoring. ◦ Enlever les blocages. ◦ Coaching / workshops / suivi. 41

Slide 54

Slide 54 text

Anna Filina • Développement. • Résolution de bogues et problèmes de performance. • Formation sur tests, Symfony et API. • Conseil sur stratégie de tests et legacy. 42

Slide 55

Slide 55 text

@afilina afilina.com