Réécriture de code vieux de 17 ans

Réécriture de code vieux de 17 ans

B3b2139e4f2c0eca4efe2379fcebc1c5?s=128

Anna Filina

October 27, 2017
Tweet

Transcript

  1. @afilina Réécriture de code vieux de 17 ans Forum PHP,

    Paris - 27 octobre 2017
  2. Anna Filina • Sauvetage de projet • Code legacy •

    Développement • Conférences • Formations privées • Contrebande d'éléPHPants
  3. Vous avez hérité d'un code de 17 ans Ça ne

    vieillit pas comme le vin.
  4. Le Web était large de 600px

  5. Vous vous sentez vieux? • Bug de l'an 2000. •

    Internet Explorer 5. • ICQ commençait à devenir cool. • Coins ronds = marque de distinction. • PHP 4!
  6. Legacy

  7. Code smells

  8. Préoccupations mixtes <?php mysql_query('...'); $total = 0; foreach ($products as

    $p) { $total += $p['qty'] * $p['price']; } ?> <p>Total: <?= $total ?></p>
  9. Variables mal nommées $a = //... $array = //... $item3

    = //...
  10. Fonctions et constantes globales include("functions.php"); MyClass::myMethod(); // une fonction dans

    un namespace? myMethod(DEV_MODE);
  11. Conditions douteuses if ($order_id > 20117) { // use this

    sql } else { // use that sql } // faiblesse = occasion pour le refactoring
  12. Longues méthodes public function importCsv($path, $googleApiKey, $databaseDsn, $databaseUser, $databasePassword) {

    // Convert CSV to array of conferences $lines = file($path); $csv = array_map('str_getcsv', $lines); $conferences = []; foreach ($csv as $line) { $conference = new Conference(); $conference->name = $line[0]; $conference->location = $line[1]; $conferences[] = $conference; // Get coordinates for location $location = urlencode($conference->location); $url = 'https://maps.googleapis.com/maps/api/geocode/json?address='.$location.'&key='.$googleApiKey; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); $response = curl_exec($curl); curl_close($curl); $json = json_decode($response); if (count($json->results) == 0) { continue; } $latitude = $json->results[0]->geometry->location->lat; $longitude = $json->results[0]->geometry->location->lng; $coordinates = $latitude.','.$longitude; $conference->coordinates = $coordinates; // Save conference to database $pdo = new PDO($databaseDsn, $databaseUser, $databasePassword); $statement = $pdo->prepare('REPLACE INTO conference (name, location, coordinates) VALUES (?, ?, ?)'); $statement->execute([ $conference->name, $conference->location, $conference->coordinates, ]);
  13. Fichier de code legacy moyen • 3000-6000 lignes de code.

    • La moitié est commentée "juste au cas". • Des méthodes de 800 lignes. • Parfois pas de classes.
  14. Code base • 5 000 classes. • 20 000 méthodes.

    • 1 500 000 lignes de code.
  15. Avant de coder

  16. Stratégie • Bâtir une stratégie selon les contraintes • Réécriture

    complète vs progressive: ◦Par classe. ◦Par feature/module. ◦Par appel HTTP. • Exécuter le code côte à côte: ◦Partage de BD/session. ◦mod_rewrite.
  17. Les données se perdent, les choses se brisent • Sauvegarde:

    test de restauration. • Anonymisez les données sensibles. • Staging: simulez les déploiements/processus batch. • Automatisez les tests avant les changements. • Faites une évaluation de risque: ◦ Ne soyez pas trop optimistes. ◦ Tenez compte des effets secondaires.
  18. Utilisez les données legacy réelles

  19. Exemple de réécriture (échec)

  20. PHP 3 vers PHP 5.6 • HTML + PHP +

    SQL dans un fichier. • Des include partout. • SQL concaténé par des if. • Tentative de réécriture: ◦ Échouée, a rendu les choses pires. ◦ Dossiers de code mort. ◦ Classes avec méthodes statiques (pas d'instances).
  21. Solution • Réécrire les formulaires complexes en Symfony: ◦ mod_rewrite

    pour les pages concernées. • Réécrire le plus gros module en orienté- objet: ◦ Extraction de design. ◦ Architecture flexible. ◦ Tests automatisés.
  22. Une fonctionnalité ne devrait pas prendre plus qu'un sprint

  23. Extraction de design

  24. Éviter d'être biaisé par le code • Ancien code →

    design. • Valider le design: ◦Clarifier les règles d'affaires. • Améliorer le design: ◦Réduire la dette technique. ◦Plus flexible. • Design → nouveau code.
  25. Correction de bogues

  26. Duplication de code • Un bogue répété 80 fois. •

    Enlever les doublons au plus vite.
  27. Corriger les méga-méthodes • Extraire la partie brisée dans sa

    propre méthode. • Écrire des tests unitaires pour la nouvelle méthode. • Réparer. • Appeller à partir de la méga-méthode.
  28. Trouver les groupes logiques Convert CSV to array of conferences.

    Get coordinates for conference location. Save conference to database. Bloc de code Commentaire Bloc de code Commentaire Bloc de code Commentaire
  29. Extraire et tester Bloc de code Méthode Bloc de code

    Commentaire Bloc de code Commentaire Appel
  30. Nommer Bloc de code Méthode Bloc de code Méthode Bloc

    de code Méthode Appel Appel Appel getConferencesFromCsv getLocationCoordinates saveConference
  31. Exemple de réécriture (succès)

  32. ASP Classic vers PHP 5.6 • Spaghetti et raccourcis (15+).

    • Langage plus supporté. • Gros ERP avec beaucoup de code.
  33. Solution • Réécrire page par page vers Symfony. • mod_rewrite

    pour les pages concernées. • Adaptateur de sessions en BD dans les deux applications. • Page dans n'importe quel langage = requête HTTP.
  34. None
  35. Tests Guzzle

  36. Tester les méthodes statiques $mock = Mockery::mock('alias:SomeClass'); $mock->shouldReceive('staticMethod')->andReturn(42);

  37. Coincé?

  38. Essayez quelque chose de nouveau • Partagez les idées. ◦

    De nouvelles personnes pour élargir la vision. • Est-ce que ça a déjà été fait? • Puis-je essayer une autre approche?
  39. À retenir • Faire une stratégie. • Vous le touchez,

    vous le refactorisez. • Utiliser des outils et des méthodologies connus. • Inspirez-vous des autres. • Le refactoring devient plus facile. • Chaque problème a une solution.
  40. Refactoring: Improving the Design of Existing Code Martin Fowler

  41. @afilina afilina.com joind.in