Réécriture de code vieux de 15 ans

Réécriture de code vieux de 15 ans

Avez-vous déjà eu à maintenir une application de 15 ans? Du code mort partout, des requêtes de base de données entre les balises HTML et quelques pages encore en PHP 3. Cette présentation de niveau stratégique vous mènera à travers une réécriture progressive depuis du code legacy à la dernière version de PHP. Apprenez à automatiser les tests de legacy, vous balader de façon transparente entre le vieux et nouveau code, et à surmonter d'autres défis qui se posent lorsqu'on travaille avec du legacy.

B3b2139e4f2c0eca4efe2379fcebc1c5?s=128

Anna Filina

July 07, 2016
Tweet

Transcript

  1. foolab.ca | @foolabca Réécriture de code vieux de 15 ans

    PHP Québec, Montréal - 7 juillet, 2016
  2. Anna Filina • Développement. • Résolution de problèmes. • Formation.

    • Conseil stratégique. • FooLab + ConFoo. 2
  3. Vous avez hérité
 d'une application de 10 ans

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

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

  6. Vous avez hérité
 d'une application de 15 ans Le code

    ne vieillit pas comme le vin
  7. Le web était 600px de large 7

  8. 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
  9. Objectifs • Réduire les erreurs. • Augmenter la vitesse de

    dev. • Réduire la dette technique. • Conseils. 9
  10. Mon expérience legacy

  11. 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
  12. Code applicatif • 5,000 classes. • 20,000 méthodes. • 1,500,000

    lignes de code. 12
  13. Prendre le code par les cornes

  14. Duplication de code • Étape 1! • Parfois, un bogue

    est répété 80+ fois. • Réutiliser le code et éliminer les duplications. 14
  15. 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
  16. Fausse sécurité 16

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

  18. Fausse sécurité $clean = htmlentities($_POST['id']); // utilisez les fonctions "filter"

    ou un framework $clean = filter_var($_POST['id'], FILTER_SANITIZE_NUMBER_INT); 16
  19. 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
  20. 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
  21. Qu'est-il arrivé à la commande 20117? 17

  22. Qu'est-il arrivé à la commande 20117? if ($order_id > 20117)

    { // utiliser cet sql } else { // utiliser cet sql } 17
  23. 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
  24. Mélange de HTML, PHP et includes 18

  25. Mélange de HTML, PHP et includes <?php include 'common.php' ?>

    <?php include 'admin.php' ?> <?php include 'user.php' ?> <table> <?php $sql = 'SELECT * FROM transactions t WHERE t.id IN('.join(',', $tids).')'; ?> 18
  26. Mélange de HTML, PHP et includes 19

  27. Mélange de HTML, PHP et includes <?php include 'common.php' ?>

    <?php include 'admin.php' ?> <?php include 'user.php' ?> <table> <?php $transactions = $user->getTransactions(); ?> 19
  28. Trop d'arguments 20

  29. Trop d'arguments protected function getMock($originalClassName, $methods = [], array $arguments

    = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false, $callOriginalMethods = false, $proxyTarget = null) { 20
  30. Trop d'arguments 21

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

    21
  32. Préparation 22

  33. Préparation • Besoin de logging. ◦ Monolog si la version

    PHP le permet. ◦ Au pire: file_put_contents. 22
  34. 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
  35. Stratégie • Élaborez une stratégie autour des contraintes. • Réécriture

    complète ou progressive. ◦ Par fichier/classe. ◦ Par module. ◦ Par appel HTTP. 23
  36. Examples contraintes ⟶ stratégie

  37. 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
  38. 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
  39. Extraction de conception

  40. É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
  41. Avant de coder....

  42. 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
  43. D'autres exemples

  44. ASP Classic à PHP 5.6 • Spaghetti et hacks de

    15+ ans. • Le language n'est plus supporté. • Gros ERP avec beaucoup de code. 32
  45. 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
  46. Tests Guzzle 34

  47. 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
  48. Solution • Séparer en serveurs 5.3 et 5.6 pour un

    nouveau départ. • REST et design patterns modernes. • Frontend en AngularJS. 36
  49. Séparer les serveurs 37

  50. Alias est votre ami (5.3 server) Alias "/module-name" "/var/www/project/angular" <Directory

    "/var/www/project/angular"> RewriteBase /module-name/ </Directory> 38
  51. Coincé?

  52. 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
  53. 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
  54. 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
  55. @afilina afilina.com