Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

You inherited a
 10-year-old codebase

Slide 4

Slide 4 text

You inherited a
 15-year-old codebase

Slide 5

Slide 5 text

You inherited a
 17-year-old codebase

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

The Web Was 600px Wide

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

Code smells

Slide 10

Slide 10 text

Mixed Concerns

Total:

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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, ]);

Slide 15

Slide 15 text

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.

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

Before you code

Slide 18

Slide 18 text

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.

Slide 19

Slide 19 text

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.

Slide 20

Slide 20 text

Rewrite example When it goes wrong.

Slide 21

Slide 21 text

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).

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

Design extraction

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

Fixing bugs

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

Rewrite example #2

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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!

Slide 34

Slide 34 text

Guzzle Tests

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

Stuck?

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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.

Slide 40

Slide 40 text

@afilina afilina.com joind.in