living with legacy code

80
Living with LegacyCode

Upload: rowan-merewood

Post on 10-Jun-2015

11.991 views

Category:

Technology


6 download

DESCRIPTION

Practical tips for dealing with projects involving legacy code. Covers investigating past projects, static analysis of existing code, and methods for changing legacy code. Presented at PHP Benelux '10

TRANSCRIPT

Page 1: Living With Legacy Code

Living

with

Legacy Code

Page 3: Living With Legacy Code

@rowan_m

Ibuildings

Plusnet

Page 4: Living With Legacy Code

What is“Legacy Code”?

Page 5: Living With Legacy Code

Code without tests

Code you've “inherited”

Code no-one understands

Technical debt

What is“Legacy Code”?

Page 6: Living With Legacy Code

Who hasnever created

Legacy Code?

Page 7: Living With Legacy Code

My own

story

little

Page 8: Living With Legacy Code

My own

story

little

Pragmatic, not idealistic

Page 9: Living With Legacy Code

Starting a project

Page 10: Living With Legacy Code

Aim to understandthe concepts

and motivations

Page 11: Living With Legacy Code

Try usingthe application

Page 12: Living With Legacy Code

Find and then readany / all

documentation

Page 13: Living With Legacy Code

Checklist

Official:Project brief ( )Requirements ( )Tech. Spec. ( )

Actual:Emails ( )Meeting notes ( )

Progress:Gant charts ( )Burndowns ( )Overtime logs ( )

Quality:Bug tracker ( )Complaints ( )User Forums ( )

Page 14: Living With Legacy Code

Talk to theoriginal developers

Page 15: Living With Legacy Code

Talk to the users

… especially the“different” ones

Page 16: Living With Legacy Code

Approachingthe code

Page 17: Living With Legacy Code

Catalogue thelive platform& environment

Page 18: Living With Legacy Code

Recreate it!

Page 19: Living With Legacy Code

Deploy the code

Page 20: Living With Legacy Code

Checklist

PHP:php.ini ( )PEAR / PECL modules ( )Compile options ( )Patches ( )

The Rest:OS ( )Package manager ( )Web server ( )Web server modules ( )Site config. ( )Database ( )Cache ( )JS libraries ( )Firewall rules ( )Proxies ( )Services running ( )

Page 21: Living With Legacy Code

Time to enterThe Code

Page 22: Living With Legacy Code

Reading

Static analysis

Dynamic analysis

Time to enterThe Code

Page 23: Living With Legacy Code

phpdoc

phpdoc -ti 'Sweet Application' \ -pp -o HTML:Smarty:PHP \ -d Libraries \ -t Docs

http://www.phpdoc.org/

Title

Style

Code in here

Docs out here!

Page 24: Living With Legacy Code

Beware of type-hiding!

Type-hinting

Type-hiding

/** * @param array $opts Current options * @return array Options with flag set */function setFlag(array $opts) { $opts['flag'] = true; return $opts;}

/** * @param int $fullPence Full price in pence * @return float Discounted price in pence */function applyDiscount($fullPence) { return ($fullPence * 0.8);}

Page 25: Living With Legacy Code

doxygen

http://www.stack.nl/~dimitri/doxygen/

doxygen -s -g ~/doxy.confvim ~/doxy.conf

# edit at least thisOUPUT_DIRECTORY# play with the rest

cd ~/dev/projectdoxygen ~/doxy.conf

Docs out here

Code in here

Page 26: Living With Legacy Code

ctags

#!/bin/bashcd ~/Dev/ &&ctags-exuberant -f ~/.vimtags \-h ".php" -R \--exclude="\.git" \--links=no \--totals=yes \--tag-relative=yes \--PHP-kinds=+cf \--regex-PHP='/abstract\s+class\s+([^ ]+)/\1/c/' \--regex-PHP='/interface\s+([^ ]+)/\1/c/' \--regex-PHP='/(public\s+|static\s+|abstract\s+|protected\s+|private\s+) ↵ function\s+\&?\s*([^ (]+)/\2/f/'

Code in here

Tags out here

Voodoo

http://ctags.sourceforge.net/

Page 27: Living With Legacy Code

bouml http://www.bouml.fr/

Page 28: Living With Legacy Code

bouml http://bouml.free.fr/

Page 29: Living With Legacy Code

bouml http://bouml.free.fr/

Page 30: Living With Legacy Code

bouml http://bouml.free.fr/

Page 31: Living With Legacy Code

bouml http://bouml.free.fr/

Page 32: Living With Legacy Code

bouml http://bouml.free.fr/

Page 33: Living With Legacy Code

bouml

Page 34: Living With Legacy Code

codesniffer

rowan@swordbean:~/Dev/ZendFramework-1.9.4/library/Zend/Service$ phpcs \ --standard=Zend Exception.php

FILE: /home/rowan/Dev/ZendFramework-1.9.4/library/Zend/Service/Exception.php--------------------------------------------------------------------------------FOUND 1 ERROR(S) AND 2 WARNING(S) AFFECTING 3 LINE(S)-------------------------------------------------------------------------------- 17 | WARNING | Line exceeds 80 characters; contains 87 characters 32 | WARNING | Line exceeds 80 characters; contains 87 characters 36 | ERROR | Opening class brace must be on a line by itself 36 | ERROR | Closing brace must be on a line by itself--------------------------------------------------------------------------------

http://pear.php.net/package/PHP_CodeSniffer/

Page 35: Living With Legacy Code

Continuo

us

Integrat

ion

http://jenkins-ci.org/

http://phpundercontrol.org/

http://sismo.sensiolabs.org/

Page 36: Living With Legacy Code

Decision time!

Page 37: Living With Legacy Code

Decision time!

Ignore it,code anyway

Rewrite,

refactor

,

test, tes

t, test

Page 38: Living With Legacy Code

Ignore it,code anyway

Page 39: Living With Legacy Code

Ignore it,code anyway

Please don't.

Page 40: Living With Legacy Code

Ignore it,code anyway

Please don't.

however...

Page 41: Living With Legacy Code

Deadlines, clients,money, etc.

Page 42: Living With Legacy Code

Deadlines, clients,money, etc.

Secure afollow-up project

Make everyoneaware of the risks

Page 43: Living With Legacy Code

Why do you need the code?

Page 44: Living With Legacy Code

Why do you need the code?

Library dependency

Adding new behaviour

Changing behaviour

simple

complex

Page 45: Living With Legacy Code

Isolatelegacy dependencies

Page 46: Living With Legacy Code

Isolatelegacy dependencies

Create ananti-corruption layer

Page 47: Living With Legacy Code

Complete isolation

Create alegacy service

Page 48: Living With Legacy Code

Partial isolation

Wrapper classesor methods

Page 49: Living With Legacy Code

<?phpinclude('/home/victorvon/secrets.inc');

/** * @param array $person willing volunteer */function extract_brain(&$person) { $brain = $person['brain']; unset($person['brain']);

return $brain;}

/** * @param array $person * @return bool living or not */function create_life($person) { require_once(LIB_DIR.'../nuts_n_bolts.inc'); kerzap(); $person['living'] = true; return $person;}?>

Some codeyou need

:(

Wrapper class

Page 50: Living With Legacy Code

class VictorWrapper{

public function __construct() {

require_once '/home/victorvon/tragedy.php';}

public function extractBrain(Person $p) {// format to legacy style$pLgcy = $this->toArray($p);// run legacy code$bLgy = extract_brain($pLgcy);// format to new style$p = $this->toPerson($pLgcy);$b = $this->toBrain($bLgcy);return array($p, $b);

}

public function createLife(Person $p) {

// validateif ($person->isAlive()) throw new LivingException();// format to legacy style$pLgcy = $this->toArray($p);// run legacy code$pLgy = create_life($pLgcy);// format to new stylereturn $this->toPerson($pLgcy);

}}

Some codeyou can use

:)

Wrapper class

Page 51: Living With Legacy Code

Changing the code

Page 52: Living With Legacy Code

Changing the code

Take anincremental approach

Commit to 1 dayat a time

Page 53: Living With Legacy Code

Why?

Difficult to estimate

Hidden dependencies

Unknown behaviour

Page 54: Living With Legacy Code

1. Get it intoversion control

Page 55: Living With Legacy Code

2. Identify theinflection point

Page 56: Living With Legacy Code

3. Create integration& acceptance tests

Page 57: Living With Legacy Code

4. Set up yourcontinuous integration

environment

Page 58: Living With Legacy Code

5. Rewrite and refactor!

Page 59: Living With Legacy Code

Types

of

changes

Page 60: Living With Legacy Code

Mixed Procedural→

Page 61: Living With Legacy Code

includes

HTML PHP

PHP

HTML

HTML

PHP

HTML

HTML

HTML

PHP

PHPPHP

HTML

PHP

PHP HTML

HTML

Page 62: Living With Legacy Code

includes

HTML PHP

PHP

HTML

HTML

PHP

HTML

HTML

HTML

PHP

PHPPHP

HTML

includes

HTML echo

foreach

foreach

HTML

HTML

HTML

echo

ifif

HTML

PHP

PHP HTML

HTML

HTML

HTML echo

echo HTML

HTML

function

function

function

Page 63: Living With Legacy Code

Procedural OO→

Page 64: Living With Legacy Code

includes

function

function

function

free code

function

Page 65: Living With Legacy Code

includes

static method

static method

static method

static method

static method

includes

function

function

function

free code

function

Page 66: Living With Legacy Code

includes

static method

static method

static method

static method

static method

includes

method

method

method

method

constructor

includes

function

function

function

free code

function

Page 67: Living With Legacy Code

Sprout method / class

Page 68: Living With Legacy Code

public function createInvoice(Account $acc, array $charges){ $invoice = new Invoice();

foreach ($charges as $chg) { $invoice->addLine($chg->getDesc(), $chg->getAmount()); }

return $invoice;}

The existing code

“We just need to be able to give each client their own personal discount on certain charges.”

Page 69: Living With Legacy Code

public function createInvoice(Account $acc, array $charges){ $invoice = new Invoice();

foreach ($charges as $chg) { $invoice->addLine($chg->getDesc(), $chg->getAmount()); }

return $invoice;}

private function calcDiscount(Account $acc, Charge $chg){ $accDisc = new AccountDiscounter($acc);

$discountedCharge = $accDisc->calculate($chg); return $discountedCharge;}

The new code

Page 70: Living With Legacy Code

public function createInvoice(Account $acc, array $charges){ $invoice = new Invoice();

foreach ($charges as $chg) {

// Sprout new behaviour!$chg = $this->calcDiscount($acc, $chg);

$invoice->addLine($chg->getDesc(), $chg->getAmount()); }

return $invoice;}

private function calcDiscount(Account $acc, Charge $chg){ $accDisc = new AccountDiscounter($acc);

$discountedCharge = $accDisc->calculate($chg); return $discountedCharge;}

Call it

Page 71: Living With Legacy Code

Untestable OO testable OO→

Page 72: Living With Legacy Code

Dependency Inversion / Extraction

Page 73: Living With Legacy Code

The Problempublic function calcDiscount(Account $acc, Charge $chg){ $accDisc = new AccountDiscounter($acc);

$discountedCharge = $accDisc->calculate($chg); return $discountedCharge;} Untestable!

Page 74: Living With Legacy Code

The Problempublic function calcDiscount(Account $acc, Charge $chg){ $accDisc = new AccountDiscounter($acc);

$discountedCharge = $accDisc->calculate($chg); return $discountedCharge;} Untestable!

class AccountDiscounter{ public function __construct(Account $acc) { // check cache // contact the database // call a web service }}

Page 75: Living With Legacy Code

Quick Solutionpublic function calcDiscount(Account $acc, Charge $chg){ $accDisc = $this->getAccountDiscounter($acc);

$discountedCharge = $accDisc->calculate($chg); return $discountedCharge;}

protected function getAccountDiscounter(Account $acc){ return new AccountDiscounter($acc);}

Mock object in your testOverride method

Page 76: Living With Legacy Code

Dependency Injection Solution

public function calcDiscount(Account $acc, Charge $chg){ $accDisc = $this->discounter;

$discountedCharge = $accDisc->calculate($chg); return $discountedCharge;}

public function __construct(AccountDiscounter $ad){ $this->discounter = $ad;}

Pass it into the class

Page 77: Living With Legacy Code

(v2) Dependency Injection Solution→

public function calcDiscount(Account $acc, Charge $chg){ $accDisc = $this->discounter;

$discountedCharge = $accDisc->calculate($chg); return $discountedCharge;}

public function __construct(IAccountDiscounter $ad){ $this->discounter = $ad;}

Make an interface

Page 78: Living With Legacy Code

Analyse

Plan

Test

IsolatE

Change

Test MORE

Summary

Page 79: Living With Legacy Code

Any questions?

Feedback to:

https://joind.in/6020

@rowan_m

Page 80: Living With Legacy Code

Credits

http://www.flickr.com/photos/flatlinevision/1514971535/http://commons.wikimedia.org/wiki/File:Weird_Tales_November_1950.jpghttp://commons.wikimedia.org/wiki/File:AdventuresIntoDarkness1401.jpghttp://www.flickr.com/photos/locationscout/3594432797/http://www.flickr.com/photos/rawhead/3466304669/http://commons.wikimedia.org/wiki/File:Rocket_to_the_Moon_54893.jpghttp://commons.wikimedia.org/wiki/File:Weird_Chills_July.jpghttp://commons.wikimedia.org/wiki/File:Terrific_01.jpghttp://commons.wikimedia.org/wiki/File:Strange_Fantasy_01.jpghttp://commons.wikimedia.org/wiki/File:Beware_01.JPGhttp://www.flickr.com/photos/erokcom/2873449983/http://www.flickr.com/photos/locationscout/3594433235/http://commons.wikimedia.org/wiki/File:Weird_Chills_Sept.JPGhttp://commons.wikimedia.org/wiki/File:Weird_Comics_01.JPGhttp://www.flickr.com/photos/x-ray_delta_one/3972988193/http://commons.wikimedia.org/wiki/File:Weird_Tales_January_1950.jpghttp://commons.wikimedia.org/wiki/File:Plan_nine_from_outer_space.jpghttp://www.flickr.com/photos/javyer/3545217741/http://www.flickr.com/photos/76074333@N00/318034222/http://commons.wikimedia.org/wiki/File:Dime_Mystery_Magazine_July_1934.jpghttp://www.flickr.com/photos/quinnanya/3802177022/

Further reading:Michael FeathersMartin Fowler