Tombstones, Vampires & Discovering Dead Code

6834447bdbca70cd2390adc690c3b4f1?s=47 Chris Gmyr
October 19, 2017

Tombstones, Vampires & Discovering Dead Code

6834447bdbca70cd2390adc690c3b4f1?s=128

Chris Gmyr

October 19, 2017
Tweet

Transcript

  1. Tombstones, Vampires & Discovering Dead Code

  2. Presented by Chris Gmyr @cmgmyr

  3. None
  4. None
  5. What are we going to learn? → What is technical

    debt? → Efficiently find dead code → Remove code with confidence
  6. What is technical debt? The implied cost of additional rework

    caused by choosing an easy solution now instead of using a better approach. If technical debt is not repaid, it accumulates 'interest', making it harder to implement changes later on...
  7. None
  8. What is technical debt? ...Technical debt is not necessarily a

    bad thing, and sometimes is required to move projects forward. → https://en.wikipedia.org/wiki/Technical_debt
  9. What does technical debt look like?

  10. Crowdpac Codebase → Laravel 4.2 → ~300 Classes → ~50,000

    Logical lines of code → 667.13 Average system complexity score → (excludes: migrations, tests, views)
  11. None
  12. How do we start paying back our technical debt?

  13. Pay back technical debt by → remove commented out blocks

    of code → improve your test suite (or start one) → refactor code you're currently working on → removing dead code
  14. What is "dead code"? → not referenced → referenced, not

    executed → no longer provides business value
  15. Great, let's remove some code!

  16. PHP "gotchas" → Dynamic language → Magic methods → Easy

    to write "bad" code
  17. Variable Functions function foo() { echo "In foo()<br />\n"; }

    $func = 'foo'; $func(); We can't search for references of foo()
  18. Variable Functions function displayName($name) { echo $name; } $func =

    'display' . ucwords($_GET['property']); $func($_GET['property_value']); How are we easily going to search for displayName() usages?
  19. Magic Methods __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(),

    __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone() and __debugInfo() http://php.net/manual/en/language.oop5.magic.php
  20. Magic Methods class User { public function __toString() { return

    $this->fullName(); } } echo new User('Chris Gmyr'); echo new User(['full_name' => 'Chris Gmyr']); $person = new User([...]); // User::make([...]); echo $person;
  21. Framework "Magic" class User { public function setPasswordAttribute($value) { $this->attributes['password']

    = Hash::make($value); } public function getPasswordAttribute($value) { return 'HIDDEN'; } }
  22. Framework "Magic" User::create([ 'password' => $_POST['password'] // setPasswordAttribute() ]); $user->password

    = $_POST['password']; // setPasswordAttribute() echo $user->password; // getPasswordAttribute()
  23. Framework "Magic" class Post { public function scopePopular($query) { return

    $query->where('votes', '>', 100); } } $posts = Post::popular()->get(); // scopePopular()
  24. Searching for usages is inefficient and error-prone

  25. Let's try this again...

  26. Using tombstones to find dead code

  27. What is a tombstone? A marker (function) placed inside code

    that is assumed dead. When a tombstone is executed, tracking information is written to a log. public function reallyOldMethod() { tombstone('2017-10-31', 'cmgmyr'); //... }
  28. What is a vampire? Suspected dead code, marked with a

    tombstone, that is alive echo $user->fullName(); class User { public function fullName() // vampire! { tombstone('2017-10-31', 'cmgmyr'); // remove this return $this->first_name . ' ' . $this->last_name; } }
  29. PHP Implementation https://github.com/scheb/tombstone function tombstone($date, $author, $label = null) {

    try { $trace = TraceProvider::getTraceHere(); $streamHandler = new StreamHandler(dirname(__DIR__)."/logs/tombstones.log"); GraveyardProvider::getGraveyard()->addHandler($streamHandler); GraveyardProvider::getGraveyard()->tombstone($date, $author, $label, $trace); } catch (\Exception $e) { // } }
  30. PHP Implementation https://github.com/scheb/tombstone-analyzer

  31. None
  32. Other Tombstone Implementations JavaScript https://blog.bugsnag.com/javascript-refactoring-with- bugsnag-and-tombstones/ Perl http://devblog.nestoria.com/post/115930183873/ tombstones-for-dead-code

  33. Let's set some tombstones!

  34. class Post { public function publishToMySpace() { tombstone('2017-10-31', 'cmgmyr'); //...

    } }
  35. None
  36. class User { public function canPublishPost() { if ($this->isAwesome()) {

    if($this->type == 'zombie') { tombstone('2017-10-31', 'cmgmyr'); //... } //... } //... } }
  37. Add tombstones to suspected dead code

  38. None
  39. ...or ALL code...

  40. None
  41. ...and wait...

  42. Check the logs 2017-11-01 09:00:00 Vampire detected from tombstone('2017-10-31', 'cmgmyr')

    in User.php on line XXX in canPublishPost() ... 2017-11-02 09:00:00 Vampire detected from tombstone('2017-10-31', 'cmgmyr') in User.php on line XXX in canPublishPost() ... 2017-11-03 09:00:00 Vampire detected from tombstone('2017-10-31', 'cmgmyr') in User.php on line XXX in canPublishPost() ...
  43. class User { public function canPublishPost() { if ($this->isAwesome()) {

    if($this->type == 'zombie') { tombstone('2017-10-31', 'cmgmyr'); //... } //... } //... } }
  44. Check the logs 2017-11-01 09:00:00 Vampire detected from tombstone('2017-10-31', 'cmgmyr')

    in User.php on line XXX in canPublishPost() ... 2017-11-02 09:00:00 Vampire detected from tombstone('2017-10-31', 'cmgmyr') in User.php on line XXX in canPublishPost() ... 2017-11-03 09:00:00 Vampire detected from tombstone('2017-10-31', 'cmgmyr') in User.php on line XXX in canPublishPost() ... 2017-11-04 ... 2017-11-05 ... 2017-11-06 ... No more canPublishPost() vampires!!!
  45. class Post { public function publishToMySpace() { tombstone('2017-10-31', 'cmgmyr'); //...

    } }
  46. class TombstoneWorkflow { public function __construct() { $this->makePlan(); $this->cleanCode(); }

    public function cleanCode() { $this->setTombstones(); while($this->codebaseHasVampires()) { $this->checkLogs(); $this->removeVampires(); } $this->reviewRemainingTombstones(); $this->removeDeadCode(); } }
  47. Crowdpac 2,400 initial tombstones (class methods) -750 locally -925 staging/production

    ~700 Remaining (30%)
  48. None
  49. Who should utilize tombstones? → legacy code → complex domain

    → inherited codebase → upgrading a codebase
  50. So, why tombstones? → Low impact solution → Use with

    dynamic languages → High confidence in removing code
  51. Thank you!

  52. Say "Hi"! twitter.com/cmgmyr github.com/cmgmyr chrisgmyr.com