Living With Legacy Code

  • 11,096 views
Uploaded on

Practical tips for dealing with projects involving legacy code. Covers investigating past projects, static analysis of existing code, and methods for changing legacy code. …

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

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
11,096
On Slideshare
0
From Embeds
0
Number of Embeds
14

Actions

Shares
Downloads
88
Comments
2
Likes
10

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. vingLiwithLegacy Code
  • 2. @rowan_m
  • 3. @rowan_mPlusnet Ibuildings
  • 4. What is“Legacy Code”?
  • 5. What is“Legacy Code”? Code without tests Code youve “inherited” Code no-one understands Technical debt
  • 6. Who has never createdLegacy Code?
  • 7. My own littlestory
  • 8. Pragmatic, not ideal isticMy own littlestory
  • 9. Starting a project
  • 10. Aim to understand the concepts and motivations
  • 11. Try usingthe application
  • 12. Find and then read any / all documentation
  • 13. Official: CheProject brief ( ) c kliRequirements ( ) stTech. Spec. ( )Actual:Emails ( )Meeting notes ( )Progress:Gant charts ( )Burndowns ( )Overtime logs ( )Quality:Bug tracker ( )Complaints ( )User Forums ( )
  • 14. Talk to theoriginal developers
  • 15. Talk to the users … especially the “different” ones
  • 16. Approaching the code
  • 17. Catalogue thelive platform& environment
  • 18. Recreate it!
  • 19. Deploy the code
  • 20. PHP: Chephp.ini ( ) c kliPEAR / PECL modules ( ) stCompile options ( )Patches ( )The Rest:OS ( )Package manager ( )Web server ( )Web server modules ( )Site config. ( )Database ( )Cache ( )JS libraries ( )Firewall rules ( )Proxies ( )Services running ( )
  • 21. Time to enter The Code
  • 22. Time to enter The Code ReadingStatic analysisDynamic analysis
  • 23. http://www.phpdoc.org/ docphp Title phpdoc -ti Sweet Application -pp -o HTML:Smarty:PHP -d Libraries Style -t Docs Code in here Docs out here!
  • 24. Beware of type-hiding! Type-hinting/** * @param array $opts Current options * @return array Options with flag set */function setFlag(array $opts) { $opts[flag] = true; return $opts;} Type-hiding /** * @param int $fullPence Full price in pence * @return float Discounted price in pence */ function applyDiscount($fullPence) { return ($fullPence * 0.8); }
  • 25. ygendox doxygen -s -g ~/doxy.conf vim ~/doxy.conf # edit at least thisCode in here OUPUT_DIRECTORY # play with the rest cd ~/dev/project Docs out here doxygen ~/doxy.conf http://www.stack.nl/~dimitri/doxygen/
  • 26. http://ctags.sourceforge.net/ ta gsc Code in here#!/bin/bash Tags out herecd ~/Dev/ &&ctags-exuberant -f ~/.vimtags -h ".php" -R --exclude=".git" --links=no --totals=yes --tag-relative=yes --PHP-kinds=+cf --regex-PHP=/abstracts+classs+([^ ]+)/1/c/ --regex-PHP=/interfaces+([^ ]+)/1/c/ --regex-PHP=/(publics+|statics+|abstracts+|protecteds+|privates+) ↵ functions+&?s*([^ (]+)/2/f/ Voodoo
  • 27. ml http://www.bouml.fr/bou
  • 28. ml http://bouml.free.fr/bou
  • 29. ml http://bouml.free.fr/bou
  • 30. ml http://bouml.free.fr/bou
  • 31. ml http://bouml.free.fr/bou
  • 32. ml http://bouml.free.fr/bou
  • 33. ou mlb
  • 34. ffer esnico drowan@swordbean:~/Dev/ZendFramework-1.9.4/library/Zend/Service$ phpcs --standard=Zend Exception.phpFILE: /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/
  • 35. t in uousCon io nInte grat http://jenkins-ci.org/ http://phpundercontrol.org/ http://sismo.sensiolabs.org/
  • 36. Decisi on tim e!
  • 37. Decisi on tim e! Igncod ore e a it , nyw ay a cto r, ref est ite, st, t Rewr , te test
  • 38. Ignore it,code anyway
  • 39. Ignore it,code anywayPlease dont.
  • 40. Ignore it,code anywayPlease dont. however...
  • 41. Deadlines, clients, money, etc.
  • 42. Deadlines, clients, money, etc. Make everyoneaware of the risks Secure afollow-up project
  • 43. Why do you need the code?
  • 44. Why do you need the code? simple Library dependency Adding new behaviour Changing behaviour complex
  • 45. Isolatelegacy dependencies
  • 46. Isolatelegacy dependencies Create ananti-corruption layer
  • 47. Complete isolation Create a legacy service
  • 48. Partial isolation Wrapper classes or methods
  • 49. Wrapper class<?phpinclude(/home/victorvon/secrets.inc);/** * @param array $person willing volunteer */function extract_brain(&$person) { $brain = $person[brain]; unset($person[brain]);} return $brain; Some code/** you need * @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;}?>
  • 50. class VictorWrapper{ Wrapper class 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); Some code $b = $this->toBrain($bLgcy); return array($p, $b); you can use } :) public function createLife(Person $p) { // validate if ($person->isAlive()) throw new LivingException(); // format to legacy style $pLgcy = $this->toArray($p); // run legacy code $pLgy = create_life($pLgcy); // format to new style return $this->toPerson($pLgcy); }}
  • 51. Changing the code
  • 52. Changing the code Take an incremental approach Commit to 1 day at a time
  • 53. Why?Difficult to estimateHidden dependencies Unknown behaviour
  • 54. 1. Get it intoversion control
  • 55. 2. Identify theinflection point
  • 56. 3. Create integration & acceptance tests
  • 57. 4. Set up yourcontinuous integration environment
  • 58. 5. Rewrite and refactor!
  • 59. Types ofchanges
  • 60. Mixed → Procedural
  • 61. includes HTML PHP HTMLHTML PHP HTML PHP HTML PHP PHP HTML PHP HTML HTML PHP HTML PHP
  • 62. includes function function function includes HTML PHP HTML HTML echo HTMLHTML PHP HTML HTML echo HTML PHP HTML PHP if HTML if PHP foreach HTML PHP HTML HTML echo HTML HTML PHP HTML HTML echo HTML PHP foreach
  • 63. Procedural → OO
  • 64. includesfunctionfunctionfunctionfunctionfree code
  • 65. includes includesfunction static methodfunction static methodfunction static methodfunction static methodfree code static method
  • 66. includes includes includesfunction static method constructorfunction static method methodfunction static method methodfunction static method methodfree code static method method
  • 67. Sprout method / class
  • 68. 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 giveeach client their own personal discount on certain charges.”
  • 69. public function createInvoice(Account $acc, array $charges){ $invoice = new Invoice(); foreach ($charges as $chg) { $invoice->addLine($chg->getDesc(), $chg->getAmount()); } return $invoice;} The new codeprivate function calcDiscount(Account $acc, Charge $chg){ $accDisc = new AccountDiscounter($acc); $discountedCharge = $accDisc->calculate($chg); return $discountedCharge;}
  • 70. public function createInvoice(Account $acc, array $charges){ $invoice = new Invoice(); Call it 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;}
  • 71. Untestable OO → testable OO
  • 72. Dependency Inversion / Extraction
  • 73. The Problempublic function calcDiscount(Account $acc, Charge $chg){ $accDisc = new AccountDiscounter($acc); $discountedCharge = $accDisc->calculate($chg); return $discountedCharge;} Untestable!
  • 74. 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 }}
  • 75. Quick Solutionpublic function calcDiscount(Account $acc, Charge $chg){ $accDisc = $this->getAccountDiscounter($acc); $discountedCharge = $accDisc->calculate($chg); return $discountedCharge;} Mock object in your test Override methodprotected function getAccountDiscounter(Account $acc){ return new AccountDiscounter($acc);}
  • 76. Dependency Injection Solutionpublic function __construct(AccountDiscounter $ad){ $this->discounter = $ad;} Pass it into the classpublic function calcDiscount(Account $acc, Charge $chg){ $accDisc = $this->discounter; $discountedCharge = $accDisc->calculate($chg); return $discountedCharge;}
  • 77. (v2) → Dependency Injection Solutionpublic function __construct(IAccountDiscounter $ad){ $this->discounter = $ad;} Make an interfacepublic function calcDiscount(Account $acc, Charge $chg){ $accDisc = $this->discounter; $discountedCharge = $accDisc->calculate($chg); return $discountedCharge;}
  • 78. Summary Analyse Plan Test IsolatE ChangeTest MORE
  • 79. Any questions? Feedback to: https://joind.in/6020 @rowan_m
  • 80. Further reading: Michael Feathers Martin Fowlerhttp://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.jpg ts dihttp://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/ C re