Rewriting 15-Year-Old Code

Rewriting 15-Year-Old Code

B3b2139e4f2c0eca4efe2379fcebc1c5?s=128

Anna Filina

October 17, 2017
Tweet

Transcript

  1. @afilina Rewriting 15-Year-Old Code Mtl.rb - October 17, 2017

  2. Anna Filina • Project rescue expert • Legacy fixer •

    Developer • Conference speaker • Trainer
  3. You inherited a
 17-year-old codebase Code doesn't age like wine.

  4. The Web Was 600px Wide

  5. Feeling Old Yet? • Y2K bug. • Internet Explorer 5.

    • ICQ was starting to be cool. • Rounded corners = status symbol.
  6. Code smells

  7. Mixed Concerns <% products = Product.all total = 0 products.each

    do |product| total += product.qty * product.price end %> <p>Total: <%= total %></p>
  8. Poorly Named Variables a = #... array = #... item3

    = #...
  9. Global Functions & Constants require_relative '../../functions.rb' MyClass::my_method(param) # namespaced function?

    my_method(DEV_MODE)
  10. Inexplicable Conditions if order_id > 20117 # use this sql

    else # use that sql end # weakness = time to refactor
  11. 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, ]);
  12. 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.
  13. Codebase • 5,000 classes. • 20,000 methods. • 1,500,000 lines

    of code.
  14. Before you code

  15. Strategy • Make a strategy based on constraints. • Full

    rewrite vs progressive: ◦By class. ◦By feature. ◦By HTTP call. • How to run code side-by-side: ◦DB/Session sharing. ◦mod_rewrite.
  16. 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.
  17. Build With Real Legacy Data

  18. (Failed) rewrite example

  19. 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).
  20. Solution • Rewrite complex forms using a framework: ◦ mod_rewrite

    for concerned pages. • Rewrite biggest feature as OOP: ◦ Design extraction. ◦ Flexible architecture. ◦ Automated tests.
  21. New feature should not take longer than a sprint.

  22. Design extraction

  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.
  24. Fixing bugs

  25. Code Duplication • Sometimes, the bug is repeated 80+ times.

    • Remove duplications ASAP.
  26. Fix Long Methods • Extract broken part into its own

    method. • Write unit tests for it. • Fix it. • Call it from the mega-method.
  27. 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
  28. Extract & Test Code block Comment Code block Method Code

    block Comment Call method
  29. Name Code block Method Code block Method Call method Call

    method Code block Method Call method conferences_from_csv location_coordinates save_conference
  30. Rewrite example #2

  31. ASP Classic to PHP 5.6 • 15+ spaghetti and hacks.

    • Language no longer supported. • Huge ERP with lots of code.
  32. Solution • Rewrite page by page to a framework. •

    mod_rewrite for concerned pages. • DB session adapter in both apps. • Page in any language = HTTP request. ◦ Automated testing possible!
  33. HTTP Tests

  34. On Testing Before you code You better test So that

    on weekends You may rest. -- Me
  35. Stuck?

  36. None
  37. Try Something New • Bounce ideas. ◦ New people to

    avoid tunnel vision. • Has this been done before? • Can I try another approach?
  38. 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.
  39. Refactoring: Improving the Design of Existing Code Martin Fowler

  40. @afilina afilina.com joind.in