Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Rewriting 15-Year-Old Code

Rewriting 15-Year-Old Code

Did you ever have to maintain a fifteen year old application? Dead code everywhere, database queries in between HTML tags and some pages still in PHP 3. This strategy-level presentation will lead you through a progressive rewrite from very old legacy to the latest shiny version of PHP. Learn how to automate legacy testing, how to seamlessly jump between the old and new parts, and how to overcome other challenges that arise from dealing with legacy.

Anna Filina

May 30, 2017
Tweet

More Decks by Anna Filina

Other Decks in Programming

Transcript

  1. @afilina
    Rewriting 15-Year-Old Code
    IPC, Berlin - May 30, 2017

    View full-size slide

  2. Anna Filina
    • whoami():\LegacyFixer
    • Project rescue expert
    • Dev, trainer, speaker

    View full-size slide

  3. You inherited a

    10-year-old codebase

    View full-size slide

  4. You inherited a

    15-year-old codebase

    View full-size slide

  5. You inherited a

    17-year-old codebase

    View full-size slide

  6. You inherited a

    17-year-old codebase
    Code doesn't age like wine.

    View full-size slide

  7. The Web Was 600px Wide

    View full-size slide

  8. Feeling Old Yet?
    • Y2K bug.
    • Internet Explorer 5.
    • ICQ was starting to be cool.
    • Rounded corners = status symbol.

    View full-size slide

  9. Mixed Concerns
    mysql_query('...');
    $total = 0;
    foreach ($products as $p) {
    $total += $p['qty'] * $p['price'];
    }
    ?>
    Total: = $total ?>

    View full-size slide

  10. Poorly Named Variables
    $a = //...
    $array = //...
    $item3 = //...

    View full-size slide

  11. Global Functions & Constants
    include("functions.php");
    MyClass::myMethod(); // namespaced function?
    myMethod(DEV_MODE);

    View full-size slide

  12. Inexplicable Conditions
    if ($order_id > 20117) {
    // use this sql
    } else {
    // use that sql
    }
    // weakness = time to refactor

    View full-size slide

  13. Long Methods
    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,
    ]);

    View full-size slide

  14. Average Legacy Code File
    • 3000-6000 lines of code.
    • Half of it is commented "in case we need
    it later".
    • Method length of 800 lines.
    • Abuse of helper classes.
    • Sometimes no classes at all.

    View full-size slide

  15. Codebase
    • 5,000 classes.
    • 20,000 methods.
    • 1,500,000 lines of code.

    View full-size slide

  16. Before you code

    View full-size slide

  17. Strategy
    • Make a strategy based on
    constraints.
    • Full rewrite vs progressive:
    ◦By class.
    ◦By feature/module.
    ◦By HTTP call.
    • How to run code side-by-side:
    ◦Session sharing.
    ◦mod_rewrite.

    View full-size slide

  18. Data Can Be Lost, Stuff Can Break
    • Backup: test restore.
    • Nullify sensitive data.
    • Staging: simulate deployments/
    upgrades/batch processes.
    • Automate tests before code
    changes.
    • Make a risk assessment:
    ◦ Don't be too optimistic.
    ◦ Account for side-effects.

    View full-size slide

  19. Rewrite example
    When it goes wrong.

    View full-size slide

  20. PHP 3 to PHP 5.6
    • HTML + PHP + SQL in same file.
    • Includes all over the place.
    • IFs that concatenate SQL.
    • Previous rewrite attempt:
    ◦ Failed, made things worse.
    ◦ Folders of dead code.
    ◦ Classes with static functions (no instances).

    View full-size slide

  21. Solution
    • Rewrite complex forms in Symfony:
    ◦ mod_rewrite for concerned pages.
    • Rewrite biggest module as OOP:
    ◦ Design extraction.
    ◦ Flexible architecture.
    ◦ Automated tests.

    View full-size slide

  22. Design extraction

    View full-size slide

  23. Avoid Code Bias
    • Old code → design docs.
    • Validate design docs:
    ◦Clarify business rules.
    • Improve design:
    ◦Reduce technical debt.
    ◦More flexible.
    • Design docs → new code.

    View full-size slide

  24. Code Duplication
    • Sometimes, the bug is
    repeated 80+ times.
    • Remove duplications
    ASAP.

    View full-size slide

  25. Fix Long Methods
    • Extract broken part into its own method.
    • Write unit tests for it.
    • Fix it.
    • Call it from the mega-method.

    View full-size slide

  26. Spot Logical Groups
    Code block
    Comment
    Code block
    Comment
    Convert CSV to array of conferences.
    Get coordinates for conference location.
    Save conference to database.
    Code block
    Comment

    View full-size slide

  27. Extract & Test
    Code block
    Comment
    Code block
    Method
    Code block
    Comment
    Call method

    View full-size slide

  28. Name
    Code block
    Method
    Code block
    Method
    Call method
    Call method
    Code block
    Method
    Call method
    getConferencesFromCsv
    getLocationCoordinates
    saveConference

    View full-size slide

  29. Rewrite example #2

    View full-size slide

  30. ASP Classic to PHP 5.6
    • 15+ spaghetti and hacks.
    • Language no longer supported.
    • Huge ERP with lots of code.

    View full-size slide

  31. Solution
    • Rewrite page by page to framework.
    • mod_rewrite for concerned pages.
    • DB session adapter in both apps.
    • Page in any language = HTTP request.
    ◦ Guzzle tests FTW!

    View full-size slide

  32. Guzzle Tests

    View full-size slide

  33. On Testing
    Before you code
    You better test
    So that on weekends
    You may rest.
    -- Anna Filina

    View full-size slide

  34. Testing Static Methods
    $mock = Mockery::mock('alias:SomeClass');
    $mock->shouldReceive('staticMethod')->andReturn(42);

    View full-size slide

  35. Try Something New
    • Bounce ideas.
    ◦ New people to avoid tunnel vision.
    • Has this been done before?
    • Can I try another approach?

    View full-size slide

  36. Takeaways
    • Make a strategy.
    • You touch it, you refactor it.
    • Use known tools & methodologies.
    • Get inspiration from others.
    • Refactoring gets easier.
    • Every problem has a solution.

    View full-size slide

  37. @afilina afilina.com
    joind.in

    View full-size slide