Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Bringing your application into 2014

Bringing your application into 2014

This should probably be titled "A practical guide to surviving legacy code"

Slides with speaker notes can be found at http://cl.ly/470i2k2O2T1U

Bbf9decfbfc2ab5b450ec503749ded28?s=128

Michael Heap

October 05, 2014
Tweet

Transcript

  1. Bringing your application into 2014

  2. Refactoring your application in 2014

  3. Refactoring your application

  4. A practical guide to surviving legacy code

  5. A practical guide to surviving any code

  6. I’m Michael! • @mheap on Twitter • Fifth time at

    PHPNW! • Developer at…
  7. None
  8. Let me paint you a picture

  9. Job interview

  10. Job offer

  11. 10am Start

  12. Awesome workstation

  13. $dbhost = 'localhost:3036';! $dbuser = 'root';! $dbpass = 'rootpassword';! $conn

    = mysql_connect($dbhost, $dbuser, $dbpass);! if(! $conn )! {! die('Could not connect: ' . mysql_error());! }! $sql = 'SELECT id, name FROM employee';! ! mysql_select_db('test_db');! $retval = mysql_query( $sql, $conn );! if(! $retval )! {! die('Could not get data: ' . mysql_error());! }! while($row = mysql_fetch_array($retval, MYSQL_ASSOC))! {! echo "EMP ID :{$row['id']} <br> ".! "EMP NAME : {$row['name']} <br> ".! "--------------------------------<br>";! } ! echo "Fetched data successfully\n";! mysql_close($conn);!
  14. The harsh truth

  15. All code is legacy code

  16. Not about intentions

  17. Fixing it

  18. Disclaimer

  19. Why put the effort in?

  20. Reduce complexity

  21. Increase maintainability

  22. Improve performance

  23. Better stability

  24. Prerequisites

  25. Refactor != Rewrite

  26. No new functionality

  27. Modern PHP

  28. Version control

  29. Choose a standard

  30. Leave the code a little better than you found it

  31. PSR

  32. PSR0/4

  33. PSR2

  34. Request lifecycle

  35. Dependency

  36. Action

  37. Response

  38. What about our code?

  39. <?php! ! $dbhost = 'localhost:3036';! $dbuser = 'root';! $dbpass =

    'rootpassword';! $conn = mysql_connect($dbhost, $dbuser, $dbpass);! if(! $conn )! {! die('Could not connect: ' . mysql_error());! }! $sql = 'SELECT id name FROM employee';! ! mysql_select_db('test_db');! $retval = mysql_query( $sql, $conn );! if(! $retval )! {! die('Could not get data: ' . mysql_error());! }! while($row = mysql_fetch_array($retval, MYSQL_ASSOC))! {! echo "EMP ID :{$row['id']} <br> ".! "EMP NAME : {$row['name']} <br> ".! "--------------------------------<br>";! } ! echo "Fetched data successfully\n";! mysql_close($conn);! ?>!
  40. Write a test

  41. Run the test

  42. <?php! ! $dbhost = 'localhost:3036';! $dbuser = 'root';! $dbpass =

    'rootpassword';! $conn = mysql_connect($dbhost, $dbuser, $dbpass);! if(! $conn )! {! die('Could not connect: ' . mysql_error());! }! $sql = 'SELECT id, name FROM employee';! ! mysql_select_db('test_db');! $retval = mysql_query( $sql, $conn );! if(! $retval )! {! die('Could not get data: ' . mysql_error());! }! while($row = mysql_fetch_array($retval, MYSQL_ASSOC))! {! echo "EMP ID :{$row['id']} <br> ".! "EMP NAME : {$row['name']} <br> ".! "--------------------------------<br>";! } ! echo "Fetched data successfully\n";! mysql_close($conn);! ?>!
  43. <?php! ! $dbhost = 'localhost:3036';! $dbuser = 'root';! $dbpass =

    'rootpassword';! $conn = mysql_connect($dbhost, $dbuser, $dbpass);! if(! $conn )! {! die('Could not connect: ' . mysql_error());! }! $sql = 'SELECT id, name FROM employee';! ! mysql_select_db('test_db');! $retval = mysql_query( $sql, $conn );! if(! $retval )! {! die('Could not get data: ' . mysql_error());! }! while($row = mysql_fetch_array($retval, MYSQL_ASSOC))! {! echo "EMP ID :{$row['id']} <br> ".! "EMP NAME : {$row['name']} <br> ".! "--------------------------------<br>";! } ! echo "Fetched data successfully\n";! mysql_close($conn);! ?>!
  44. …! ! mysql_select_db('test_db');! $retval = mysql_query( $sql, $conn );! if(!

    $retval )! {! die('Could not get data: ' . mysql_error());! }! while($row = mysql_fetch_array($retval, MYSQL_ASSOC))! {! echo "EMP ID :{$row['id']} <br> ".! "EMP NAME : {$row['name']} <br> ".! "--------------------------------<br>";! } ! ! …!
  45. …! ! mysql_select_db('test_db');! $retval = mysql_query( $sql, $conn );! if(!

    $retval )! {! die('Could not get data: ' . mysql_error());! }! ! $data = array();! ! while($row = mysql_fetch_array($retval, MYSQL_ASSOC))! {! ! $data[] = $row;! }! ! foreach ($data as $row)! echo "EMP ID :{$row['id']} <br> ".! "EMP NAME : {$row['name']} <br> ".! "--------------------------------<br>";! } ! ! …
  46. …! ! $data = array(! ! array("id" => 1, "name"

    => "Michael ")! );! ! foreach ($data as $row)! echo "EMP ID :{$row['id']} <br> ".! "EMP NAME : {$row['name']} <br> ".! "--------------------------------<br>";! } ! ! …!
  47. <?php! ! $dbhost = 'localhost:3036';! $dbuser = 'root';! $dbpass =

    'rootpassword';! $conn = mysql_connect($dbhost, $dbuser, $dbpass);! if(! $conn )! {! die('Could not connect: ' . mysql_error());! }! $sql = 'SELECT id, name FROM employee';! ! mysql_select_db('test_db');! $retval = mysql_query( $sql, $conn );! if(! $retval )! {! die('Could not get data: ' . mysql_error());! }! ! while($row = mysql_fetch_array($retval, MYSQL_ASSOC))! {! ! $data[] = $row;! }! ! include 'employee_view.php';! ! mysql_close($conn);! ?>!
  48. <?php! ! $data = array(! ! array("id" => 1, "name"

    => "Michael ")! );! ! include 'employee_view.php';! ! ?>!
  49. <?php! ! $dbhost = 'localhost:3036';! $dbuser = 'root';! $dbpass =

    'rootpassword';! $conn = mysql_connect($dbhost, $dbuser, $dbpass);! if(! $conn )! {! die('Could not connect: ' . mysql_error());! }! $sql = 'SELECT id, name FROM employee';! ! mysql_select_db('test_db');! $retval = mysql_query( $sql, $conn );! if(! $retval )! {! die('Could not get data: ' . mysql_error());! }! ! while($row = mysql_fetch_array($retval, MYSQL_ASSOC))! {! ! $data[] = $row;! }! ! include 'employee_view.php';! ! mysql_close($conn);! ?>!
  50. <?php! ! $dbhost = 'localhost:3036';! $dbuser = 'root';! $dbpass =

    'rootpassword';! $conn = mysql_connect($dbhost, $dbuser, $dbpass);! if(! $conn )! {! die('Could not connect: ' . mysql_error());! }! $sql = 'SELECT id, name FROM employee';! ! mysql_select_db('test_db');! $retval = mysql_query( $sql, $conn );! if(! $retval )! {! die('Could not get data: ' . mysql_error());! }! ! while($row = mysql_fetch_array($retval, MYSQL_ASSOC))! {! ! $data[] = $row;! }! ! include 'employee_view.php';! ! mysql_close($conn);! ?>!
  51. <?php! ! include ‘db_setup.php';! ! $sql = 'SELECT id, name

    FROM employee';! ! $retval = mysql_query( $sql, $conn );! if(! $retval )! {! die('Could not get data: ' . mysql_error());! }! ! while($row = mysql_fetch_array($retval, MYSQL_ASSOC))! {! ! $data[] = $row;! }! ! include 'employee_view.php';! ! include ‘db_teardown.php';! ?>!
  52. <?php! ! include ‘db_setup.php';! ! $sql = 'SELECT id, name

    FROM employee_archive’;! ! $retval = mysql_query( $sql, $conn );! if(! $retval )! {! die('Could not get data: ' . mysql_error());! }! ! while($row = mysql_fetch_array($retval, MYSQL_ASSOC))! {! ! $data[] = $row;! }! ! include 'employee_view.php';! ! include ‘db_teardown.php';! ?>!
  53. <?php! ! class Db {! ! public function __construct($user, $password,

    $database) {! ! ! // Connection logic goes here! ! }! ! ! public function query($sql) {! ! ! $retval = mysql_query( $sql, $this->conn );! ! ! if(! $retval )! ! ! {! ! ! die('Could not get data: ' . mysql_error());! ! ! }! ! ! ! while($row = mysql_fetch_array($retval, MYSQL_ASSOC))! ! ! {! ! ! ! $data[] = $row;! ! ! }! ! ! ! return $data;! ! ! }! }!
  54. <?php! ! include 'db_setup.php';! ! $data = $db->query('SELECT id, name

    FROM employee');! include 'employee_view.php';! ! include 'db_teardown.php';! ?>!
  55. <?php! ! class EmployeeRepo {! ! public function getAllEmployees() {!

    ! ! global $db;! ! ! return $db->query('SELECT id, name FROM employee');! ! }! }!
  56. <?php! ! class EmployeeRepo {! ! public function __construct() {!

    ! ! global $db;! ! ! $this->db = $db;! ! }! ! ! public function getAllEmployees() {! ! ! return $this->db->query('SELECT id, name FROM employee');! ! }! }!
  57. <?php! ! include 'db_setup.php';! include 'employee_repo.php';! ! $repo = new

    EmployeeRepo;! $data = $repo->getAllEmployees();! include 'employee_view.php';! ! include 'db_teardown.php';! ?>!
  58. class Db

  59. /src/Db.php

  60. class Foo_Bar_Baz

  61. /src/Foo/Bar/Baz.php

  62. class \mheap\Foo

  63. src mheap/Foo.php

  64. <?php! ! include ‘vendor/autoloader.php’;! include ‘db_setup.php’;! ! $repo = new

    EmployeeRepo;! $data = $repo->getAllEmployees();! include 'employee_view.php';! ?>!
  65. <?php! ! class EmployeeRepo {! ! public function __construct() {!

    ! ! global $db;! ! ! $this->db = $db;! ! }! ! ! public function getAllEmployees() {! ! ! return $this->db->query('SELECT id, name FROM employee');! ! }! }!
  66. <?php! ! class EmployeeRepo {! ! public function __construct($db) {!

    ! ! $this->db = $db;! ! }! ! ! public function getAllEmployees() {! ! ! return $this->db->query(‘SELECT id, name FROM employee');! ! }! }!
  67. <?php! ! include ‘vendor/autoload.php';! include ‘db_setup.php’;! ! $repo = new

    EmployeeRepo($db);! $data = $repo->getAllEmployees();! include 'employee_view.php';! ?>!
  68. <?php! ! include 'vendor/autoload.php';! include 'db_setup.php';! ! $repo = new

    EmployeeRepo($db);! $data = $repo->getAllEmployees();! ! $response = new Response();! ob_start();! include 'employee_view.php';! $reponse->content = ob_get_contents();! ob_end_clean();! ! $response->send();! ?>!
  69. <?php! ! $this->assertEquals($response->content, "EMP ID: ...etc");! ! ?>!

  70. And more!

  71. Do it alongside your app

  72. None
  73. None
  74. Review

  75. Dependency

  76. Action

  77. Response

  78. Small, reusable classes

  79. Don’t change behaviour

  80. Useful tools

  81. PHPCS

  82. PHPMD

  83. PHPCPD

  84. “Don’t Repeat Yourself” was never about code. It’s about knowledge.

    It’s about cohesion. If two pieces of code represent the exact same knowledge, they will always change together. Having to change them both is risky: you might forget one of them. On the other hand, if two identical pieces of code represent different knowledge, they will change independently. De-duplicating them introduces risk, because changing the knowledge for one object, might accidentally change it for the other object. - http://verraes.net/2014/08/dry-is-about-knowledge/
  85. Qafoo Refactoring Tools

  86. Scrutinizer CI

  87. The parts that didn’t fit

  88. Testing legacy code

  89. Working with external dependencies

  90. Most of the PHP-FIG work

  91. Logging strategies

  92. Automation

  93. Effective version control

  94. Find me later

  95. Parting thoughts

  96. Perfect is the enemy of better

  97. Optimise for reading code

  98. $ordersWithProducts rather than $data.

  99. markReadAndNotifyUser() rather than markRead(true)

  100. Constants are good

  101. Code doesn't lie

  102. Any questions?

  103. Thank you http://joind.in/11802