Theory and practice – migrating your
legacy code into our modern test
driven development world.

Hartmann, Jankowfsky, Rin...
Legacy Code?

Wikipedia says „Legacy code is source code that
 relates to a no-longer supported or
 manufactured operating...
Agenda
- Where are we now, and why?
- Ammunition!
- Refactor!


Bad News? -> Workshop




Jankowfsky, Rinne – Mayflower/sw...
Who we are
Lars Jankowfsky:
- CTO and (Co)Founder swoodoo.com
- (Co)Founder of OXID eSales. Refactored OXID eShop during 1...
Who are you?
- What‘s your profession?
- Sofware company or agency?
- What‘s your team size?
- Using MVC?
- Who does other...
What about your projects?
- What‘s your average project lifetime?
- Is there PHP code more than 5 years old?
- How many li...
Typical problems?
- Typical legacy applications
- Started some years ago with PHP 4
- written in Spaghetti code
- half pro...
PHP, made in 2000
- no coding standards
- no PHPDoc
- no Design Patterns
- few separation of concerns
- has been changed a...
Big ball of mud
http://en.wikipedia.org/wiki/Big_ball_of_mud


A Big Ball of Mud is a haphazardly structured,
sprawling, s...
Enough Ammunition?
- change requests get more and more expensive
- bug rate is increasing
- clearly a dead-end street!
- t...
Enough Ammunition?
Ever heard things like this?


„Only X can fix that.“
„It will take ages to fix it.“
„Changing this but...
Enough Ammunition?


60-80 % of all development effort is maintenance

http://elearning.tvm.tcs.co.in/SMaintenance/SMainte...
What you should never do!
Please don‘t try a complete rewrite!


- Too expensive
- Takes too long
- the old codebase is us...
Remember?

    Netscape 6?                                Rewrite....


    dBase for Windows?                         Rew...
joel in 2000



  „When you throw away code and start from scratch, you are
  throwing away all that knowledge. All those ...
Test Driven Adoption
1. Unit tests for existing code with PHPUnit
2. experience of confidence in own code
3. Insight: Test...
PHP and Unit Testing
- Layout & UI code is hard to unit-test,
  acceptance-test instead
- test maintenance costs:
  - unit...
Refactoring?
- Modifying code without changing it‘s behaviour
- „cleaning up“


     “Refactoring is the process of changi...
Team?
- experience in Test driven Development?
- „know how vs. understanding“
- In PHP? It‘s different to the Java World!
...
Let‘s start.
- Identify the nastiest, ugliest and...
- probably most important piece of code and
  let‘s start with this o...
COURAGE
Jankowfsky, Rinne – Mayflower/swoodoo
Modifying without... ?????
- if you refactor you need tests to proove that
  you did not break any functionality
- Have te...
And now ?
- Write tests first.
- You will need to refactor your application while
  writing tests.


                     ...
While refactoring ...
- adjust coding style
- add missing documentation
- remove redundant code / copy & paste-code
- remo...
Spaghetti Code?
- Very old code, maybe developed in
  the last PHP 3 century
- a lot of redundant copy-paste code
- missin...
Spaghetti Code?
function getThema($id, $lang)
{
  global $db, $PMF_LANG;

    $result = $db->query(sprintf(quot;SELECT the...
Spaghetti Code - strategy
- Identify recurring code parts and implement
   classes
- Use of standard libraries like Zend F...
„Half procedual –halb
                                object-orientated“
- Code with different quality
- Just a few docume...
„Half procedual –half object-
                              orientated“ - strategy
- Add inline documentation for all clas...
PHP 4 OOP
- Application was developed using „object-
  orientated“ PHP 4
- Using of
    - PHP 4 references
    - Re-declar...
PHP 4 OOP - strategy
- Maybe you‘re lucky and there are no problems.
  Maybe.
- If you see problems, they are fatal errors...
Global Problems
- OOP, Public, Private ?
- Globals ?
- Super Globals...
- Session
- Cookies




Jankowfsky, Rinne – Mayflo...
Proxy for testing protected methods


     public function getProxy($superClassName)
     {
       $proxyClassName = quot;...
Global ?


   class someOtherClass {
      var $setting;

       function calculateSomething($a, $b) {
          return $a...
class someOtherClass {
   private $setting;

    public function calculateSomething($a, $b) {
      return $a+$b;
    }

 ...
Dependencies...
- Separate Logic from View
- Create accessors, add all Parameter in calls




Jankowfsky, Rinne – Mayflowe...
Dependencies...

   class displayUserDetails()
   {
      /**
       * Processes input and sends user first name, last nam...
/**
 * A view class responsible for displaying user details.
 */
class userView()
{
    /**
     * Loads user object and s...
Fixtures
- Make sure that tests do not alter fixture.
- Fixture is FIXture
- if you feel that creating fixtures is too muc...
Fixtures the ruby way...
- Ruby uses „YAML Ain’t Markup Language“
- http://www.yaml.org/
- PHP YAML support done via Syck
...
yaml - loading

     public static function create($fileName)
      {
        $fileName = 'Fixtures'.DIRECTORY_SEPARATOR.$...
yaml - storing

 public static function load($fixtures, $tableName)
   {
      if (is_array($fixtures) && count($fixtures)...
yaml - cleanup

 if (!empty(self::$_usedTables)) {
         foreach (array_reverse(self::$_usedTables) as $tableName) {
  ...
Fixtures the other side...
- manual fixtures are too much work?
- use a test database
- think about automatic creation of ...
Mocking Stubs?
      „...may simulate the behavior of existing code (such as a
      procedure on a remote machine) or be ...
Stubs


- Unit testing is about testing a unit of work, not
  a complete workflow
- isolates your code from external depen...
Stubs
 /**
  * The PHPUnit way
  */
/**
 * A simple stub providing a simple result directly instead of using the database
...
Mock Objects
- Helpful tool to fake complex objects
- Useful to mock service apis, external
  software, ...
 /**
  * The P...
About Mocking
- a better separation of concerns helps
  - writing less mock objects
  - writing easier mock objects
- if t...
Golden rules
- know your budget: what are your maintenance
  costs? What are the things you can‘t do now?
- there is no si...
Tools?
- CruiseControl for continous integration
    - PHPUnit
    - SeleniumRC and SeleniumIDE
    - PHP Code Sniffer
   ...
Questions?




Jankowfsky, Rinne – Mayflower/swoodoo
Thank you for your interest!



              eMail: lars.jankowfsky@swoodoo.com
                     thorsten.rinne@mayfl...
Upcoming SlideShare
Loading in...5
×

Theory and practice – migrating your legacy code into our modern test driven development world.

4,542

Published on

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
4,542
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
77
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Theory and practice – migrating your legacy code into our modern test driven development world.

  1. 1. Theory and practice – migrating your legacy code into our modern test driven development world. Hartmann, Jankowfsky, Rinne
  2. 2. Legacy Code? Wikipedia says „Legacy code is source code that relates to a no-longer supported or manufactured operating system or other computer technology. The term can also mean code inserted into modern software for the purpose of maintaining an older or previously supported feature“ Jankowfsky, Rinne – Mayflower/swoodoo
  3. 3. Agenda - Where are we now, and why? - Ammunition! - Refactor! Bad News? -> Workshop Jankowfsky, Rinne – Mayflower/swoodoo
  4. 4. Who we are Lars Jankowfsky: - CTO and (Co)Founder swoodoo.com - (Co)Founder of OXID eSales. Refactored OXID eShop during 1.5 years with 10 developers. Thorsten Rinne: - Senior Developer & Team Lead at Mayflower GmbH - Founder and main developer of phpMyFAQ Johann Peter Hartmann: - CTO and Founder of Mayflower GmbH - CEO and Founder of SektionEins GmbH Jankowfsky, Rinne – Mayflower/swoodoo
  5. 5. Who are you? - What‘s your profession? - Sofware company or agency? - What‘s your team size? - Using MVC? - Who does other languages, too? - Using agile methods? - Using continuous integration? - Using unit tests? Jankowfsky, Rinne – Mayflower/swoodoo
  6. 6. What about your projects? - What‘s your average project lifetime? - Is there PHP code more than 5 years old? - How many lines of code? - How many change requests per year? - Has there been a specification? - Were all features in the first released version implemented like they‘re specified in the specification? Jankowfsky, Rinne – Mayflower/swoodoo
  7. 7. Typical problems? - Typical legacy applications - Started some years ago with PHP 4 - written in Spaghetti code - half procedual, half object-orientated - „PHP 4“ OOP - using old, unmaintained libraries like PEAR::DB Jankowfsky, Rinne – Mayflower/swoodoo
  8. 8. PHP, made in 2000 - no coding standards - no PHPDoc - no Design Patterns - few separation of concerns - has been changed a lot - no refactoring, because „it worked“ - updated to run with php 4 in 2003 - updated to run with php 5 in 2006 Jankowfsky, Rinne – Mayflower/swoodoo
  9. 9. Big ball of mud http://en.wikipedia.org/wiki/Big_ball_of_mud A Big Ball of Mud is a haphazardly structured, sprawling, sloppy, duct-tape-and-baling-wire, spaghetti-code jungle. These systems show unmistakable signs of unregulated growth, and repeated, expedient repair.... Jankowfsky, Rinne – Mayflower/swoodoo
  10. 10. Enough Ammunition? - change requests get more and more expensive - bug rate is increasing - clearly a dead-end street! - team motivation decreases - hard to bring in new members into the team - deprecated functions cause problems in future PHP releases Jankowfsky, Rinne – Mayflower/swoodoo
  11. 11. Enough Ammunition? Ever heard things like this? „Only X can fix that.“ „It will take ages to fix it.“ „Changing this button will take two weeks.“ „I don‘t want to work for this project.“ „I don‘t want to touch this code.“ „I don‘t know how this bug could reappear.“ Jankowfsky, Rinne – Mayflower/swoodoo
  12. 12. Enough Ammunition? 60-80 % of all development effort is maintenance http://elearning.tvm.tcs.co.in/SMaintenance/SMaintenance/6.htm http://www.bitpipe.com/detail/RES/1138902960_291.html Jankowfsky, Rinne – Mayflower/swoodoo
  13. 13. What you should never do! Please don‘t try a complete rewrite! - Too expensive - Takes too long - the old codebase is used, tested & bugfixed - Developers love to rewrite: new code is more fun, code is easier to write than to read Jankowfsky, Rinne – Mayflower/swoodoo
  14. 14. Remember? Netscape 6? Rewrite.... dBase for Windows? Rewrite.... Quattro Pro? Rewrite.... Access refatored... Excel Jankowfsky, Rinne – Mayflower/swoodoo
  15. 15. joel in 2000 „When you throw away code and start from scratch, you are throwing away all that knowledge. All those collected bug fixes. Years of programming work.“ http://www.joelonsoftware.com/articles/fog0000000069.html Jankowfsky, Rinne – Mayflower/swoodoo
  16. 16. Test Driven Adoption 1. Unit tests for existing code with PHPUnit 2. experience of confidence in own code 3. Insight: Tests are easier if written before software 4. Insight: Tests help documenting the code 5. Insight: Tests define the real API Jankowfsky, Rinne – Mayflower/swoodoo
  17. 17. PHP and Unit Testing - Layout & UI code is hard to unit-test, acceptance-test instead - test maintenance costs: - unit test work fine with stable APIs - high change rate in PHP results in API changes - tests need to be changed, too - slows down development, increases initial development costs - ... but your software survives more than 4 years Jankowfsky, Rinne – Mayflower/swoodoo
  18. 18. Refactoring? - Modifying code without changing it‘s behaviour - „cleaning up“ “Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure.” (Martin Fowler) Jankowfsky, Rinne – Mayflower/swoodoo
  19. 19. Team? - experience in Test driven Development? - „know how vs. understanding“ - In PHP? It‘s different to the Java World! - Developers are conservative. They do not like any changes. How much use still vi or emacs? - Courage? - You need to make sure that everybody understands TDD before you start. Jankowfsky, Rinne – Mayflower/swoodoo
  20. 20. Let‘s start. - Identify the nastiest, ugliest and... - probably most important piece of code and let‘s start with this one. - if you take the easy files you won‘t solve the critical issues and... - move the risk to the end. Jankowfsky, Rinne – Mayflower/swoodoo
  21. 21. COURAGE Jankowfsky, Rinne – Mayflower/swoodoo
  22. 22. Modifying without... ????? - if you refactor you need tests to proove that you did not break any functionality - Have tests first. Then change code. - legacy code ? There are no tests!! Jankowfsky, Rinne – Mayflower/swoodoo
  23. 23. And now ? - Write tests first. - You will need to refactor your application while writing tests. - Write selenium tests for your application. - no :( Jankowfsky, Rinne – Mayflower/swoodoo
  24. 24. While refactoring ... - adjust coding style - add missing documentation - remove redundant code / copy & paste-code - remove unused(!) code - maintain a list of future todos with priorities Jankowfsky, Rinne – Mayflower/swoodoo
  25. 25. Spaghetti Code? - Very old code, maybe developed in the last PHP 3 century - a lot of redundant copy-paste code - missing separation of concerns - No or just minor separation of code and layout - No use of libraries like PEAR, Zend Framework or eZ components - No or outdated documentation - No tests at all Jankowfsky, Rinne – Mayflower/swoodoo
  26. 26. Spaghetti Code? function getThema($id, $lang) { global $db, $PMF_LANG; $result = $db->query(sprintf(quot;SELECT thema FROM %sfaqdata WHERE id = %d AND lang = '%s'quot;, SQLPREFIX, $id, $lang)); if ($db->num_rows($result) > 0) { while ($row = $db->fetch_object($result)) { $output = htmlentities($row->thema); } } else { $output = $PMF_LANG[quot;no_catsquot;]; } return $output; } phpMyFAQ 1.3.x 2002/2003 Jankowfsky, Rinne – Mayflower/swoodoo
  27. 27. Spaghetti Code - strategy - Identify recurring code parts and implement classes - Use of standard libraries like Zend Framework or eZ components - Add inline documentation - Fix your coding styles! - Add unittests for the new, refactored backend - Add Selenium tests for the frontend Jankowfsky, Rinne – Mayflower/swoodoo
  28. 28. „Half procedual –halb object-orientated“ - Code with different quality - Just a few documentation - Maybe some tests ... maybe ... - „the typical current PHP 4 project“ - Found everywhere! Really everywhere! Jankowfsky, Rinne – Mayflower/swoodoo
  29. 29. „Half procedual –half object- orientated“ - strategy - Add inline documentation for all classes and methods - Improve the re-using of duplicate code - Add unittests and Selenium tests - Improve every code part with PHP 5 functions, for example using file_put_contents() instead of fopen(), fwrite(), and fclose(). Jankowfsky, Rinne – Mayflower/swoodoo
  30. 30. PHP 4 OOP - Application was developed using „object- orientated“ PHP 4 - Using of - PHP 4 references - Re-declaration of $this Jankowfsky, Rinne – Mayflower/swoodoo
  31. 31. PHP 4 OOP - strategy - Maybe you‘re lucky and there are no problems. Maybe. - If you see problems, they are fatal errors like - Objects are referenced by value - $foo =& new Foo(); - Solution: - Implement unittests - UsestandardAPIs - Fix the PHP 5 problems Jankowfsky, Rinne – Mayflower/swoodoo
  32. 32. Global Problems - OOP, Public, Private ? - Globals ? - Super Globals... - Session - Cookies Jankowfsky, Rinne – Mayflower/swoodoo
  33. 33. Proxy for testing protected methods public function getProxy($superClassName) { $proxyClassName = quot;{$superClassName}Proxyquot;; if (!class_exists($proxyClassName)) { $class = <<<CLASS class $proxyClassName extends $superClassName { public function __call($function, $args) { $function = str_replace('protected_', '_', $function); return call_user_func_array(array(&$this, $function), $args); } } CLASS; eval($class); } return new $proxyClassName(); } Jankowfsky, Rinne – Mayflower/swoodoo
  34. 34. Global ? class someOtherClass { var $setting; function calculateSomething($a, $b) { return $a+$b; } } class myOldNastyClass { function needToTestThisFunction() { $class = new someOtherClass(); $z = $_GET['input']; // .... return $class->calculateSomething( $class->setting, $z); } } Jankowfsky, Rinne – Mayflower/swoodoo
  35. 35. class someOtherClass { private $setting; public function calculateSomething($a, $b) { return $a+$b; } public function setSetting($set) { $this->setting = $set; } public function getSetting() { return $this->setting; } } class myInput { public function getParameter($name) { return $_GET[$name]; } } class myOldNastyClass { private $input; // set e.g. in constructor public function needToTestThisFunction(someOtherClass &$class, $z) { $z = $input->getParameter('input'); // .... return $class->calculateSomething( $class->getSetting(), $z); } } Jankowfsky, Rinne – Mayflower/swoodoo
  36. 36. Dependencies... - Separate Logic from View - Create accessors, add all Parameter in calls Jankowfsky, Rinne – Mayflower/swoodoo
  37. 37. Dependencies... class displayUserDetails() { /** * Processes input and sends user first name, last name to display; */ function show() { global $dbLink; global $templateEngine; $itemId = (int) $_REQUEST['user_id']; $firstName = $dbLink->getOne(quot;select first_name from users where id = $itemIdquot;); $lastName = $dbLink->getOne(quot;select last_name from users where id = $itemIdquot;); $templateEngine->addTemplateVar('firstName', $firstName); $templateEngine->addTemplateVar('lastName', $lastName); $templateEngine->display(); } } Jankowfsky, Rinne – Mayflower/swoodoo
  38. 38. /** * A view class responsible for displaying user details. */ class userView() { /** * Loads user object and sends first name, last name to display */ public function show() { $userId = $this->_inputProcessor->getParameter(quot;user_idquot;); $this->templateEngine->addTemplateVar('user', $this->model->loadUser(userId)); $this->templateEngine->display(); } } /** * And the corresponding model */ class userModel() { public function loadUser($userId) { $user = new User( $userId ); return array( 'firstName' => $user->getFirstName(), 'lastName' => $user->getLastName()); } } Jankowfsky, Rinne – Mayflower/swoodoo
  39. 39. Fixtures - Make sure that tests do not alter fixture. - Fixture is FIXture - if you feel that creating fixtures is too much work - refactor more! - Do never let tests leave altered data! Jankowfsky, Rinne – Mayflower/swoodoo
  40. 40. Fixtures the ruby way... - Ruby uses „YAML Ain’t Markup Language“ - http://www.yaml.org/ - PHP YAML support done via Syck - Syck = YAML + fast. - http://whytheluckystiff.net/syck/ - http://www.frontalaufprall.com/2008/05/05/ Jankowfsky, Rinne – Mayflower/swoodoo
  41. 41. yaml - loading public static function create($fileName) { $fileName = 'Fixtures'.DIRECTORY_SEPARATOR.$fileName; ob_start(); include $fileName; $fileContents = ob_get_contents(); ob_clean(); $yamlData = syck_load($fileContents); return $yamlData; } Jankowfsky, Rinne – Mayflower/swoodoo
  42. 42. yaml - storing public static function load($fixtures, $tableName) { if (is_array($fixtures) && count($fixtures)) { foreach ($fixtures as $fixture) { if (is_array($fixture) && is_array(current($fixture))) { Fixtures::load($fixture, $tableName); } $fields = array_keys($fixture); $statement = quot;INSERT INTO $tableName (quot; . implode(', ', $fields) . quot;) VALUES (:quot; . implode(quot;, :quot;, $fields) . quot;)quot;; $stmt = self::$_db->prepare($statement); if (count($fixture)) { foreach ($fixture as $key => $value ) { $stmt->bindValue(':'.$key, $value); } } $stmt->execute(); self::$_usedTables[$tableName] = $tableName; } } } Jankowfsky, Rinne – Mayflower/swoodoo
  43. 43. yaml - cleanup if (!empty(self::$_usedTables)) { foreach (array_reverse(self::$_usedTables) as $tableName) { self::$_db->execute(quot;TRUNCATE TABLE $tableNamequot;); } } Jankowfsky, Rinne – Mayflower/swoodoo
  44. 44. Fixtures the other side... - manual fixtures are too much work? - use a test database - think about automatic creation of yaml files Jankowfsky, Rinne – Mayflower/swoodoo
  45. 45. Mocking Stubs? „...may simulate the behavior of existing code (such as a procedure on a remote machine) or be a temporary substitute for yet-to-be-developed code...“ Why do we need this ? stub: einfache klasse, die so tut, als wäre sie wie das original mock: das gleiche, aber mit introspektion und von aussen Jankowfsky, Rinne – Mayflower/swoodoo konfigurierbar
  46. 46. Stubs - Unit testing is about testing a unit of work, not a complete workflow - isolates your code from external dependencies - can be done with PHPunit, but doesn‘t need to Jankowfsky, Rinne – Mayflower/swoodoo
  47. 47. Stubs /** * The PHPUnit way */ /** * A simple stub providing a simple result directly instead of using the database */ class UserModelStub extends UserModel { public getUserCount() { return 10; } } UserModelStub extends PHPUnit_Framework_Testcase { public function testGetUserCount() { $stub = $this->getMock(‘UserModel‘); $stub->expects($this->any())->method(‘getUserCount‘)->will($this->returnValue(10)); } } Jankowfsky, Rinne – Mayflower/swoodoo
  48. 48. Mock Objects - Helpful tool to fake complex objects - Useful to mock service apis, external software, ... /** * The PHPUnit way */ class UserModelTest extends PHPUnit_Framework_Testcase { public function testGetUserCountIsCalled() { $usermock = $this->getMock(‘UserModel‘); $usermock->expects($this->once())->method(‘getUserCount‘)->with($this->equalTo(ADMIN)); $admin = new AdminModel($usermock); $admin->getNumber(); } } Jankowfsky, Rinne – Mayflower/swoodoo
  49. 49. About Mocking - a better separation of concerns helps - writing less mock objects - writing easier mock objects - if there is a lot of mock objects, rethink your architecture -> refactor more! Jankowfsky, Rinne – Mayflower/swoodoo
  50. 50. Golden rules - know your budget: what are your maintenance costs? What are the things you can‘t do now? - there is no silver bullet. Introducing TDD takes A LOT of time - TDD wins on the long run, not on the short - Confident developers are efficient developers - There is no way around proper coding style and documentation - You have to rewrite code, some even twice. Jankowfsky, Rinne – Mayflower/swoodoo
  51. 51. Tools? - CruiseControl for continous integration - PHPUnit - SeleniumRC and SeleniumIDE - PHP Code Sniffer - PHP CodeBrowser Jankowfsky, Rinne – Mayflower/swoodoo
  52. 52. Questions? Jankowfsky, Rinne – Mayflower/swoodoo
  53. 53. Thank you for your interest! eMail: lars.jankowfsky@swoodoo.com thorsten.rinne@mayflower.de Jankowfsky, Rinne – Mayflower/swoodoo
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×