Living With Legacy Code

17,825 views
17,200 views

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

Presented at PHP Benelux '10

Published in: Technology
2 Comments
13 Likes
Statistics
Notes
No Downloads
Views
Total views
17,825
On SlideShare
0
From Embeds
0
Number of Embeds
14
Actions
Shares
0
Downloads
104
Comments
2
Likes
13
Embeds 0
No embeds

No notes for slide

Living With Legacy Code

  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

×